0. Библиотеки и предварительная подготовка
library(readxl)
library(dplyr)
library(scatterPlotMatrix)
library(kableExtra)
library(psych)
library(summarytools)
library(ggplot2)
library(GGally)
library(nortest)
library(moments)
library(ppcor)
library(corrplot)
library(lm.beta)
library(ellipse)
library(car)
library(olsrr)
print_df <- function(df)
{
df |>
kable(format = "html") |>
kable_styling() |>
kableExtra::scroll_box(width = "100%", height = "100%")
}
plot_qq_graph <- function(data, column_name) {
data<-data
expected_quantiles <- qnorm(ppoints(length(data)))
qqnorm(data, pch = 19, col = "deeppink")
qqline(data, distribution = qnorm, lwd = 2, col = "limegreen")
legend("topleft", legend = column_name, col = "deeppink", pch = 19)
}
plot_pp_plot <- function(data, column_name) {
n <- length(data)
expect_prob <- pnorm(data, mean(data), sqrt(var(data) * (n - 1) / n))
expect_prob <- sort(expect_prob)
plot(x = expect_prob, y = ppoints(n), col = "red")
lines(x = c(0, 1), y = c(0, 1), col = "blue")
legend("topleft", legend = column_name, col = "deeppink", pch = 19)
}
1. Загрузка и предобработка данных
df <- read_excel("stat2021_sm/Sleep/SLEEP_shortname.xls", )
df <- df |> mutate(SLEEP = ifelse(SLEEP < 0, NA, SLEEP), PARADOX = ifelse(is.infinite(PARADOX), NA, PARADOX), PRED_IND = as.factor(PRED_IND), EXP_IND = as.factor(EXP_IND), DANG_IND = as.factor(DANG_IND))
df |> slice(1:10) |> print_df()
| NAME |
BODY_WEI |
BRAIN_WE |
SLOWWAVE |
PARADOX |
SLEEP |
LIFESPAN |
GESTTIME |
PRED_IND |
EXP_IND |
DANG_IND |
| African elephant |
6654.000 |
5712.0 |
NA |
NA |
3.3 |
38.6 |
645 |
3 |
5 |
3 |
| African giant rat |
1.000 |
6.6 |
6.3 |
2.0 |
8.3 |
4.5 |
42 |
3 |
1 |
3 |
| Arctic Fox |
3.385 |
44.5 |
NA |
NA |
12.5 |
14.0 |
60 |
1 |
1 |
1 |
| Arc. ground squirrel |
0.920 |
5.7 |
NA |
NA |
16.5 |
NA |
25 |
5 |
2 |
3 |
| Asian elephant |
2547.000 |
4603.0 |
2.1 |
1.8 |
3.9 |
69.0 |
624 |
3 |
5 |
4 |
| Baboon |
10.550 |
179.5 |
9.1 |
0.7 |
9.8 |
27.0 |
180 |
4 |
4 |
4 |
| Big brown bat |
0.023 |
0.3 |
15.8 |
3.9 |
19.7 |
19.0 |
35 |
1 |
1 |
1 |
| Brazilian tapir |
160.000 |
169.0 |
5.2 |
1.0 |
6.2 |
30.4 |
392 |
4 |
5 |
4 |
| Cat |
3.300 |
25.6 |
10.9 |
3.6 |
14.5 |
28.0 |
63 |
1 |
2 |
1 |
| Chimpanzee |
52.160 |
440.0 |
8.3 |
1.4 |
9.7 |
50.0 |
230 |
1 |
1 |
1 |
2. Описание признаков
- NAME — название животного —
качественный признак
- BODY_WEI — вес тела в килограммах —
количественны непрерывный признак
- BRAIN_WE — вес мозга в граммах —
количественный непрерывный признак
- SLOWWAVE — время медленного сна, часов в
день — количественный непрерывный признак
- PARADOX —время быстрого сна, часов в день
— количественный непрерывный признак
- SLEEP — общее время сна, часов в день —
количественный непрерывный признак
- LIFESPAN — продолжительность жизни в годах
— количественный непрерывный признак
- GESTTIME — время беременности в днях —
количественный непрерывный признак
- PRED_IND — индекс хищничества (1 —
наименее вероятный объект для охоты, 5 — наиболее вероятный объект для
охоты) — порядковый признак
- EXP_IND — индекс воздействия во время сна
(1 — наименее подверженный воздействию (например, животное спит в хорошо
защищенном логове), 5 — наиболее подверженный воздействию) —
порядковый признак
- DANG_IND — общий индекс опасности (на
основе двух вышеуказанных индексов и другой информации) (1 — наименьшая
опасность (от других животных), 5 — наибольшая опасность (со стороны
других животных)) — порядковый признак
3. Матричный график разброса
categories <-list(NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1:5, 1:5, 1:5)
df |> dplyr::select(-NAME) |> scatterPlotMatrix(regressionType = 1,
corrPlotType = "Text",
slidersPosition = list(
dimCount = 7,
xStartingDimIndex = 1,
yStartingDimIndex = 1
),
categorical = categories,
plotProperties = list(noCatColor = "Indigo"),
controlWidgets = TRUE,
height = 1050, width = 1000)
Видим достаточно логичные кандидаты на логарифмирование: вес
тела, вес мозга, продолжительность
жизни и время беременности — это
мультиплекативные признаки. Также время быстрого сна
имеет перекос влево, так что лучше его тоже отлогарифмировать, хотя
логически, так как время сна содержит в себе
время быстрого сна, этот признак не является
мультиплекативным, а также не имеет нелинейную зависимость. Однако,
ассиметрия времи быстрого сна имеет достаточно большое
значение:
desc0 <- df |>dplyr::select(-NAME, -PRED_IND, -EXP_IND, -DANG_IND) |> describe() |>dplyr::select(-trimmed, -mad, -se)
print_df(desc0)
| |
vars |
n |
mean |
sd |
median |
min |
max |
range |
skew |
kurtosis |
| BODY_WEI |
1 |
62 |
198.789984 |
899.158011 |
3.3425 |
0.005 |
6654.0 |
6653.995 |
6.2494291 |
40.5981819 |
| BRAIN_WE |
2 |
62 |
283.134193 |
930.278942 |
17.2500 |
0.140 |
5712.0 |
5711.860 |
4.8288287 |
23.2367752 |
| SLOWWAVE |
3 |
48 |
8.672917 |
3.666452 |
8.3500 |
2.100 |
17.9 |
15.800 |
0.2798669 |
-0.4442636 |
| PARADOX |
4 |
50 |
1.972000 |
1.442651 |
1.8000 |
0.000 |
6.6 |
6.600 |
1.3667228 |
1.7793264 |
| SLEEP |
5 |
58 |
10.532759 |
4.606760 |
10.4500 |
2.600 |
19.9 |
17.300 |
0.1909556 |
-0.6497094 |
| LIFESPAN |
6 |
58 |
19.877586 |
18.206255 |
15.1000 |
2.000 |
100.0 |
98.000 |
1.9107968 |
5.0043782 |
| GESTTIME |
7 |
58 |
142.353448 |
146.805039 |
79.0000 |
12.000 |
645.0 |
633.000 |
1.5974241 |
2.3223297 |
4. Логарифмирование хвостатых
df.log <- df
df.log <- df.log |> mutate(BODY_WEI = log(BODY_WEI), BRAIN_WE = log(BRAIN_WE), PARADOX = log(PARADOX), LIFESPAN = log(LIFESPAN), GESTTIME = log(GESTTIME))
df.log <- df.log |> mutate(PARADOX = ifelse(is.infinite(PARADOX), NA, PARADOX))
df.log |> dplyr::select(-NAME) |> filter(!is.na(SLOWWAVE) & !is.na(PARADOX) & !is.na(SLEEP) & !is.na(LIFESPAN) & !is.na(GESTTIME)) |> scatterPlotMatrix(regressionType = 1,
corrPlotType = "Text",
slidersPosition = list(
dimCount = 7,
xStartingDimIndex = 1,
yStartingDimIndex = 1
),
categorical = categories,
plotProperties = list(noCatColor = "Indigo"),
controlWidgets = TRUE,
height = 1050, width = 1000)
Выведем также немного другой матричный график, в котором удаление NA
будет попарным.
df.log |> dplyr::select(-NAME) |> ggpairs(columns = 1:7)

Видим, что распределения стали более симметричными, причём почти все
признаки имеют отрицательный эксцесс, то есть они более “плоские”, чем
нормальное:
desc1 <- df.log |>dplyr::select(-NAME, -PRED_IND, -EXP_IND, -DANG_IND) |> describe() |>dplyr::select(-trimmed, -mad, -se)
print_df(desc1)
| |
vars |
n |
mean |
sd |
median |
min |
max |
range |
skew |
kurtosis |
| BODY_WEI |
1 |
62 |
1.3375390 |
3.1231277 |
1.2066382 |
-5.2983174 |
8.802974 |
14.101291 |
0.1453599 |
-0.5232692 |
| BRAIN_WE |
2 |
62 |
3.1401979 |
2.4465120 |
2.8477071 |
-1.9661129 |
8.650324 |
10.616437 |
0.0434421 |
-0.6184785 |
| SLOWWAVE |
3 |
48 |
8.6729167 |
3.6664517 |
8.3500000 |
2.1000000 |
17.900000 |
15.800000 |
0.2798669 |
-0.4442636 |
| PARADOX |
4 |
49 |
0.4656547 |
0.7080779 |
0.5877867 |
-1.2039728 |
1.887070 |
3.091042 |
-0.1364307 |
-0.5840339 |
| SLEEP |
5 |
58 |
10.5327586 |
4.6067604 |
10.4500000 |
2.6000000 |
19.900000 |
17.300000 |
0.1909556 |
-0.6497094 |
| LIFESPAN |
6 |
58 |
2.5930586 |
0.9435145 |
2.7120343 |
0.6931472 |
4.605170 |
3.912023 |
-0.1629152 |
-0.9151912 |
| GESTTIME |
7 |
58 |
4.4441910 |
1.0602517 |
4.3596587 |
2.4849066 |
6.469250 |
3.984344 |
0.0473325 |
-1.1407247 |
По графику неравномерностей не видно. Если раскрасить по
категориальным признакам, то тоже не получается выделить раздельные
облака.
5. Выбросы
Видим пару выбросов, для которых вес мозга мал, но продолжительность
жизни достаточно велика:
df.log |> filter(LIFESPAN > 2 & BRAIN_WE < 0) |> print_df()
| NAME |
BODY_WEI |
BRAIN_WE |
SLOWWAVE |
PARADOX |
SLEEP |
LIFESPAN |
GESTTIME |
PRED_IND |
EXP_IND |
DANG_IND |
| Big brown bat |
-3.772261 |
-1.203973 |
15.8 |
1.3609766 |
19.7 |
2.944439 |
3.555348 |
1 |
1 |
1 |
| Little brown bat |
-4.605170 |
-1.386294 |
17.9 |
0.6931472 |
19.9 |
3.178054 |
3.912023 |
1 |
1 |
1 |
Это две летучие мыши.
Построим теперь графики разброса без выбросов.
df.log.out <- df.log |> filter(!(LIFESPAN > 2 & BRAIN_WE < 0))
df.log.out |> dplyr::select(-NAME) |> filter(!is.na(SLOWWAVE) & !is.na(PARADOX) & !is.na(SLEEP) & !is.na(LIFESPAN) & !is.na(GESTTIME)) |> scatterPlotMatrix(regressionType = 1,
corrPlotType = "Text",
slidersPosition = list(
dimCount = 7,
xStartingDimIndex = 1,
yStartingDimIndex = 1
),
categorical = categories,
plotProperties = list(noCatColor = "Indigo"),
controlWidgets = TRUE,
height = 1050, width = 1000)
И сравним корреляции.
df.log |> dplyr::select(-NAME) |> ggpairs(columns = 1:7)

df.log.out |> dplyr::select(-NAME) |> ggpairs(columns = 1:7)

В основном они выросли, по крайней мере, там, где мы
исключали выбросы.
6. Зависимости от индексов опасности
Необходимо описать разницу между животными по индексу опасности
места, где они спят. Посмотрим на количество животных в каждой
группе:
df.log.out |>dplyr::select(EXP_IND) |> summary()
EXP_IND
1:25
2:13
3: 4
4: 5
5:13
Выборки для индексов \(2\) и \(5\) можем считать сбалансированными.
Визуально оценим распределения с помощью ящиков с усами:
df.log.out |> ggplot(aes(x = EXP_IND, y = BODY_WEI)) +
geom_boxplot()

df.log.out |> ggplot(aes(x = EXP_IND, y = BRAIN_WE)) +
geom_boxplot()

df.log.out |> ggplot(aes(x = EXP_IND, y = PARADOX)) +
geom_boxplot()

df.log.out |> ggplot(aes(x = EXP_IND, y = SLOWWAVE)) +
geom_boxplot()

df.log.out |> ggplot(aes(x = EXP_IND, y = SLEEP)) +
geom_boxplot()

df.log.out |> ggplot(aes(x = EXP_IND, y = LIFESPAN)) +
geom_boxplot()

df.log.out |> ggplot(aes(x = EXP_IND, y = GESTTIME)) +
geom_boxplot()

В основном видна монотонная зависимость, однако, предстоит проверить
это с помощью критериев.
7. Нормальность, вид распределения
Из-за малости выборок в третьей и четвёртой группах исключим их из
исследования.
get_df_on_ind <- function(i) {
return(df.log.out |> filter(EXP_IND == i))
}
Посмотрим сначала на гистограммы распределений по группам для каждого
признака.
for (i in c(1, 2, 5)) {
print(paste("Группа ", i))
par(mfrow = c(2, 4))
hist(get_df_on_ind(i)$BODY_WEI, main = "")
hist(get_df_on_ind(i)$BRAIN_WE, main = "")
hist(get_df_on_ind(i)$SLOWWAVE, main = "")
hist(get_df_on_ind(i)$PARADOX, main = "")
hist(get_df_on_ind(i)$SLEEP, main = "")
hist(get_df_on_ind(i)$LIFESPAN, main = "")
hist(get_df_on_ind(i)$GESTTIME, main = "")
}
[1] "Группа 1"
[1] "Группа 2"

[1] "Группа 5"


Посмотрим на нормальность с помощью Normal Probability Plot:
for (i in c(1, 2, 5)) {
print(paste("Группа ", i))
par(mfrow = c(2, 4))
plot_qq_graph(get_df_on_ind(i)$BODY_WEI, "BODY_WEI")
plot_qq_graph(get_df_on_ind(i)$BRAIN_WE, "BRAIN_WE")
plot_qq_graph(get_df_on_ind(i)$SLOWWAVE, "SLOWWAVE")
plot_qq_graph(get_df_on_ind(i)$PARADOX, "PARADOX")
plot_qq_graph(get_df_on_ind(i)$SLEEP, "SLEEP")
plot_qq_graph(get_df_on_ind(i)$LIFESPAN, "LIFESPAN")
plot_qq_graph(get_df_on_ind(i)$GESTTIME, "GESTTIME")
}
[1] "Группа 1"
[1] "Группа 2"

[1] "Группа 5"


И с помощью PP-plot:
for (i in c(1, 2, 5)) {
print(paste("Группа ", i))
par(mfrow = c(2, 4))
plot_pp_plot((get_df_on_ind(i) |> filter(!is.na(BODY_WEI)))$BODY_WEI, "BODY_WEI")
plot_pp_plot((get_df_on_ind(i) |> filter(!is.na(BRAIN_WE)))$BRAIN_WE, "BRAIN_WE")
plot_pp_plot((get_df_on_ind(i) |> filter(!is.na(SLOWWAVE)))$SLOWWAVE, "SLOWWAVE")
plot_pp_plot((get_df_on_ind(i) |> filter(!is.na(PARADOX)))$PARADOX, "PARADOX")
plot_pp_plot((get_df_on_ind(i) |> filter(!is.na(SLEEP)))$SLEEP, "SLEEP")
plot_pp_plot((get_df_on_ind(i) |> filter(!is.na(LIFESPAN)))$LIFESPAN, "LIFESPAN")
plot_pp_plot((get_df_on_ind(i) |> filter(!is.na(GESTTIME)))$GESTTIME, "GESTTIME")
}
[1] "Группа 1"
[1] "Группа 2"

[1] "Группа 5"


А теперь оценим по тесту Шапиро-Уилка:
df.shapiro.test.p.value <- data.frame(ind = c(1, 2, 5), BODY_WEI_P = rep(0, 3), BRAIN_WE_P = rep(0, 3), SLOWWAVE_P = rep(0, 3), PARADOX_P = rep(0, 3), SLEEP_P = rep(0, 3), LIFESPAN_P = rep(0, 3), GESTTIME_P = rep(0, 3))
for (i in c(1, 2, 5)) {
df.shapiro.test.p.value[df.shapiro.test.p.value$ind == i, ] <- c(i, shapiro.test(get_df_on_ind(i)$BODY_WEI)$p.value,
shapiro.test(get_df_on_ind(i)$BRAIN_WE)$p.value,
shapiro.test(get_df_on_ind(i)$SLOWWAVE)$p.value,
shapiro.test(get_df_on_ind(i)$PARADOX)$p.value,
shapiro.test(get_df_on_ind(i)$SLEEP)$p.value,
shapiro.test(get_df_on_ind(i)$LIFESPAN)$p.value,
shapiro.test(get_df_on_ind(i)$GESTTIME)$p.value)
}
df.shapiro.test.p.value |> print_df()
| ind |
BODY_WEI_P |
BRAIN_WE_P |
SLOWWAVE_P |
PARADOX_P |
SLEEP_P |
LIFESPAN_P |
GESTTIME_P |
| 1 |
0.1924485 |
0.4855703 |
0.4685556 |
0.0946346 |
0.3176217 |
0.0733372 |
0.2755650 |
| 2 |
0.0204661 |
0.0126712 |
0.4403360 |
0.8802017 |
0.4562102 |
0.8049845 |
0.5314537 |
| 5 |
0.9922347 |
0.7461442 |
0.0844404 |
0.9214902 |
0.0052604 |
0.4522227 |
0.0232529 |
И наконец по Лиллиефорсу:
df.lillie.test.p.value <- data.frame(ind = c(1, 2, 5), BODY_WEI_P = rep(0, 3), BRAIN_WE_P = rep(0, 3), SLOWWAVE_P = rep(0, 3), PARADOX_P = rep(0, 3), SLEEP_P = rep(0, 3), LIFESPAN_P = rep(0, 3), GESTTIME_P = rep(0, 3))
for (i in c(1, 2, 5)) {
df.lillie.test.p.value[df.lillie.test.p.value$ind == i, ] <- c(i, lillie.test(get_df_on_ind(i)$BODY_WEI)$p.value,
lillie.test(get_df_on_ind(i)$BRAIN_WE)$p.value,
lillie.test(get_df_on_ind(i)$SLOWWAVE)$p.value,
lillie.test(get_df_on_ind(i)$PARADOX)$p.value,
lillie.test(get_df_on_ind(i)$SLEEP)$p.value,
lillie.test(get_df_on_ind(i)$LIFESPAN)$p.value,
lillie.test(get_df_on_ind(i)$GESTTIME)$p.value)
}
df.lillie.test.p.value |> print_df()
| ind |
BODY_WEI_P |
BRAIN_WE_P |
SLOWWAVE_P |
PARADOX_P |
SLEEP_P |
LIFESPAN_P |
GESTTIME_P |
| 1 |
0.3977150 |
0.3724944 |
0.2399545 |
0.2094751 |
0.2837660 |
0.3215462 |
0.4647484 |
| 2 |
0.1209793 |
0.0760938 |
0.8789409 |
0.9154588 |
0.8195576 |
0.7890638 |
0.9238088 |
| 5 |
0.7872499 |
0.4314403 |
0.0328885 |
0.5556462 |
0.0004679 |
0.4954892 |
0.1272532 |
Этот критерий является менее мощным, однако даёт схожие
результаты.
По итогу можно считать, что распределения всех признаков во всех
группах похожи на нормальные, кроме веса тела и
веса мозга во второй группе и времени
медленного сна, времени сна и времени
беременности в пятой группе.
8. t-тесты
Сначала посмотрим на дисперсию, чтобы применить t-test для
распределений с равными дисперсиями. Тест Фишера проверяет равенство
дисперсий для нормально распределённых данных, которые мы определелили в
прошлом пункте:
df.fisher.var <- data.frame(IND1 = rep(0, 3), IND2 = rep(0, 3), BODY_WEI_P = rep(0, 3), BRAIN_WE_P = rep(0, 3), SLOWWAVE_P = rep(0, 3), PARADOX_P = rep(0, 3), SLEEP_P = rep(0, 3), LIFESPAN_P = rep(0, 3), GESTTIME_P = rep(0, 3))
k <- 1
for (i in c(1, 2, 5)){
for (j in c(1, 2, 5)){
if (i < j){
df.fisher.var[k, ] <- c(i, j,
var.test(get_df_on_ind(i)$BODY_WEI, get_df_on_ind(j)$BODY_WEI)$p.value,
var.test(get_df_on_ind(i)$BRAIN_WE, get_df_on_ind(j)$BRAIN_WE)$p.value,
var.test(get_df_on_ind(i)$SLOWWAVE, get_df_on_ind(j)$SLOWWAVE)$p.value,
var.test(get_df_on_ind(i)$PARADOX, get_df_on_ind(j)$PARADOX)$p.value,
var.test(get_df_on_ind(i)$SLEEP, get_df_on_ind(j)$SLEEP)$p.value,
var.test(get_df_on_ind(i)$LIFESPAN, get_df_on_ind(j)$LIFESPAN)$p.value,
var.test(get_df_on_ind(i)$GESTTIME, get_df_on_ind(j)$GESTTIME)$p.value)
k <- k + 1
}
}
}
df.fisher.var |> print_df()
| IND1 |
IND2 |
BODY_WEI_P |
BRAIN_WE_P |
SLOWWAVE_P |
PARADOX_P |
SLEEP_P |
LIFESPAN_P |
GESTTIME_P |
| 1 |
2 |
0.3119679 |
0.1905823 |
0.3041400 |
0.4869923 |
0.6645465 |
0.9543801 |
0.7543563 |
| 1 |
5 |
0.6380604 |
0.3940484 |
0.4168502 |
0.7837348 |
0.0334406 |
0.0080450 |
0.6722005 |
| 2 |
5 |
0.6237712 |
0.6698967 |
0.1563733 |
0.4297079 |
0.0221523 |
0.0125095 |
0.5226789 |
Гипотеза о равенстве не отвергается для веса тела и
веса мозга между первой и пятой группы, для
времени медленного сна, времени сна и
времени беременности между первой и второй группы,
времени быстрого сна между всеми группами и
продолжительности жизни между первой и второй
группой.
Проведём t-тесты для первой, второй и пятой группы по всем
признакам:
df.t.test.p.value <- data.frame(IND1 = rep(0, 3), IND2 = rep(0, 3), BODY_WEI_P = rep(0, 3), BRAIN_WE_P = rep(0, 3), SLOWWAVE_P = rep(0, 3), PARADOX_P = rep(0, 3), SLEEP_P = rep(0, 3), LIFESPAN_P = rep(0, 3), GESTTIME_P = rep(0, 3))
k <- 1
flag <- FALSE
flagBB <- FALSE
flagSSG <-FALSE
flagL <- FALSE
for (i in c(1, 2, 5)){
for (j in c(1, 2, 5)){
if (i < j){
flag <- i == 2 & j == 5
flagBB <- i == 1 & j == 5
flagSSG <- i == 1 & j == 2
flagL <- i == 1 & j == 2
df.t.test.p.value[k, ] <- c(i, j,
t.test(get_df_on_ind(i)$BODY_WEI, get_df_on_ind(j)$BODY_WEI, var.equal = flag | flagBB)$p.value,
t.test(get_df_on_ind(i)$BRAIN_WE, get_df_on_ind(j)$BRAIN_WE, var.equal = flag | flagBB)$p.value,
t.test(get_df_on_ind(i)$SLOWWAVE, get_df_on_ind(j)$SLOWWAVE, var.equal = flag | flagSSG)$p.value,
t.test(get_df_on_ind(i)$PARADOX, get_df_on_ind(j)$PARADOX, var.equal = TRUE)$p.value,
t.test(get_df_on_ind(i)$SLEEP, get_df_on_ind(j)$SLEEP, var.equal = flag | flagSSG)$p.value,
t.test(get_df_on_ind(i)$LIFESPAN, get_df_on_ind(j)$LIFESPAN, var.equal = flag | flagL)$p.value,
t.test(get_df_on_ind(i)$GESTTIME, get_df_on_ind(j)$GESTTIME, var.equal = flag | flagSSG)$p.value)
k <- k + 1
}
}
}
df.t.test.p.value |> print_df()
| IND1 |
IND2 |
BODY_WEI_P |
BRAIN_WE_P |
SLOWWAVE_P |
PARADOX_P |
SLEEP_P |
LIFESPAN_P |
GESTTIME_P |
| 1 |
2 |
0.3331031 |
0.8379613 |
0.4313905 |
0.0894578 |
0.3325090 |
0.4306579 |
0.2064574 |
| 1 |
5 |
0.0000011 |
0.0000094 |
0.0000195 |
0.0000105 |
0.0000000 |
0.0000080 |
0.0000807 |
| 2 |
5 |
0.0000004 |
0.0000050 |
0.0045885 |
0.0168625 |
0.0000441 |
0.0026227 |
0.0033338 |
Гипотеза о равенстве распределений по всем признакам между первой и
второй не отвергается, а между первой и пятой и второй и пятой
отвергается. Однако для признаков, которые мы посчитали ненормальными
(вес тела и вес мозга во второй группе
и время медленного сна, время сна и
время беременности в пятой группе) p-value нельзя
считать верным, так как размеры выборок малы и эти признаки не имеют
нормального распределения.
9. Тест Манна-Уитни
Для проверки равенства распределений для ненормально распределённых
признаков можем применить критерий Манна-Уитни, однако прежде посмотрим
на симметричност по группам:
df.skew <- data.frame(IND = c(1, 2, 5), BODY_WEI_P = rep(0, 3), BRAIN_WE_P = rep(0, 3), SLOWWAVE_P = rep(0, 3), PARADOX_P = rep(0, 3), SLEEP_P = rep(0, 3), LIFESPAN_P = rep(0, 3), GESTTIME_P = rep(0, 3))
for (i in c(1, 2, 5)){
df.skew[df.skew$IND == i, ] <- c(i, skewness(get_df_on_ind(i)$BODY_WEI, na.rm = TRUE),
skewness(get_df_on_ind(i)$BRAIN_WE, na.rm = TRUE),
skewness(get_df_on_ind(i)$SLOWWAVE, na.rm = TRUE),
skewness(get_df_on_ind(i)$PARADOX, na.rm = TRUE),
skewness(get_df_on_ind(i)$SLEEP, na.rm = TRUE),
skewness(get_df_on_ind(i)$LIFESPAN, na.rm = TRUE),
skewness(get_df_on_ind(i)$GESTTIME, na.rm = TRUE))
}
df.skew |> print_df()
| IND |
BODY_WEI_P |
BRAIN_WE_P |
SLOWWAVE_P |
PARADOX_P |
SLEEP_P |
LIFESPAN_P |
GESTTIME_P |
| 1 |
0.1672298 |
0.5645030 |
-0.0002586 |
-0.5598674 |
0.3874961 |
1.0385890 |
0.5038765 |
| 2 |
-1.4951275 |
-1.6495435 |
0.5921447 |
-0.1315432 |
0.1049490 |
0.0122452 |
0.1394734 |
| 5 |
-0.1198309 |
0.0708243 |
1.0734624 |
-0.1861544 |
1.5538884 |
0.5578426 |
-1.0319607 |
Видим, что асимметрия веса тела и веса
мозга у второй группы (ненормально распределённые признаки)
сильно (примерно на порядок) отличается от первой и пятой группы.
Аналогично: асимметрия времени сна и времени
беременности пятой группы сильно отличается от первой и пятой.
Для асимметрии времени быстрого сна у пятой группы и
первой также нет оснований говорить о приблизительном равенстве, однако
между пятой и второй группой разница мала, меньше \(0.5\), что может говорить об удовлетворении
условий для критерия, однако надо ещё посмотреть на исправленную
выборочную дисперсию:
df.var <- data.frame(IND = c(1, 2, 5), BODY_WEI_P = rep(0, 3), BRAIN_WE_P = rep(0, 3), SLOWWAVE_P = rep(0, 3), PARADOX_P = rep(0, 3), SLEEP_P = rep(0, 3), LIFESPAN_P = rep(0, 3), GESTTIME_P = rep(0, 3))
for (i in c(1, 2, 5)){
df.var[df.var$IND == i, ] <- c(i, var(get_df_on_ind(i)$BODY_WEI, na.rm = TRUE),
var(get_df_on_ind(i)$BRAIN_WE, na.rm = TRUE),
var(get_df_on_ind(i)$SLOWWAVE, na.rm = TRUE),
var(get_df_on_ind(i)$PARADOX, na.rm = TRUE),
var(get_df_on_ind(i)$SLEEP, na.rm = TRUE),
var(get_df_on_ind(i)$LIFESPAN, na.rm = TRUE),
var(get_df_on_ind(i)$GESTTIME, na.rm = TRUE))
}
df.var |> print_df()
| IND |
BODY_WEI_P |
BRAIN_WE_P |
SLOWWAVE_P |
PARADOX_P |
SLEEP_P |
LIFESPAN_P |
GESTTIME_P |
| 1 |
6.034299 |
4.526511 |
7.279905 |
0.3159571 |
12.914130 |
0.9033046 |
0.8095168 |
| 2 |
3.443109 |
2.189395 |
12.449889 |
0.4525376 |
15.658077 |
0.9042128 |
0.6633179 |
| 5 |
4.599834 |
2.815790 |
3.733333 |
0.2533922 |
3.156556 |
0.1911274 |
0.9828249 |
Видим, что дисперсии времени медленного сна для
пятой и второй группы даже приблизительно не равны, поэтому ни один из
признаков, которые мы хотели проверить не подходят для применения
критерия Манна-Уитни. То есть для них мы ничего не можем сказать о
равенстве распределений.
10. Коэффициент Пирсона
pairwise.cor <- function(data, name_method = "pearson"){
m <- cor(data)
m.p.value <- cor(data)
for (i in colnames(data)){
for (j in colnames(data)){
data.out <- data|> filter(!is.na(data[[i]]) & !is.na(data[[j]]))
m[i, j] <- cor(x = data.out[[i]], y = data.out[[j]], method = name_method)
m.p.value[i, j] <- cor.test(x = as.vector(data.out[[i]]), y = as.vector(data.out[[j]]), method = name_method)$p.value
}
}
corrplot(m, method = "number")
as.data.frame(m.p.value)
}
Перед вычислением коэффициента корреляции Пирсона посмотрим на
нормальность признаков для всех индивидов по тесту Шапиро-Уилка:
df.shapiro.test.all.p.value <- data.frame(BODY_WEI_P = 0, BRAIN_WE_P = 0, SLOWWAVE_P = 0, PARADOX_P = 0, SLEEP_P = 0, LIFESPAN_P = 0, GESTTIME_P = 0)
for (i in c(1)) {
df.shapiro.test.all.p.value[1, ] <- c(shapiro.test(df.log.out$BODY_WEI)$p.value,
shapiro.test(df.log.out$BRAIN_WE)$p.value,
shapiro.test(df.log.out$SLOWWAVE)$p.value,
shapiro.test(df.log.out$PARADOX)$p.value,
shapiro.test(df.log.out$SLEEP)$p.value,
shapiro.test(df.log.out$LIFESPAN)$p.value,
shapiro.test(df.log.out$GESTTIME)$p.value)
}
df.shapiro.test.all.p.value |> print_df()
| BODY_WEI_P |
BRAIN_WE_P |
SLOWWAVE_P |
PARADOX_P |
SLEEP_P |
LIFESPAN_P |
GESTTIME_P |
| 0.6745281 |
0.7912255 |
0.7502073 |
0.8786641 |
0.2600607 |
0.334608 |
0.1265908 |
Как видим по p-value можем считать, что распределения похожи на
нормальные, поэтому критерий проверки на значимость коэффициента
корреляции будет проверять ещё и независимость признаков, а также
коэффициент Спирмена будет примерно равен коэффициенту Пирсона.
Теперь посчитаем коэффициенты корреляции Пирсона для всех пар
признаков, причём удаление NA будем производить попарно, чтобы для
каждой пары получить максимальное количество индивидов:
df.log.out |>
mutate(BRAIN_BODY = log(exp(BRAIN_WE) / exp(BODY_WEI))) |>
dplyr::select(-NAME, -EXP_IND, -PRED_IND, -DANG_IND) |>
pairwise.cor() |>
print_df()
| |
BODY_WEI |
BRAIN_WE |
SLOWWAVE |
PARADOX |
SLEEP |
LIFESPAN |
GESTTIME |
BRAIN_BODY |
| BODY_WEI |
0.0000000 |
0.0000000 |
0.0003011 |
0.0182618 |
0.0002898 |
0.0000000 |
0.0000000 |
0.0000000 |
| BRAIN_WE |
0.0000000 |
0.0000000 |
0.0004412 |
0.0043862 |
0.0000914 |
0.0000000 |
0.0000000 |
0.0000397 |
| SLOWWAVE |
0.0003011 |
0.0004412 |
0.0000000 |
0.0000332 |
0.0000000 |
0.0008653 |
0.0000535 |
0.0414927 |
| PARADOX |
0.0182618 |
0.0043862 |
0.0000332 |
0.0000000 |
0.0000000 |
0.0060667 |
0.0000022 |
0.7322638 |
| SLEEP |
0.0002898 |
0.0000914 |
0.0000000 |
0.0000000 |
0.0000000 |
0.0000957 |
0.0000000 |
0.1188045 |
| LIFESPAN |
0.0000000 |
0.0000000 |
0.0008653 |
0.0060667 |
0.0000957 |
0.0000000 |
0.0000000 |
0.0241044 |
| GESTTIME |
0.0000000 |
0.0000000 |
0.0000535 |
0.0000022 |
0.0000000 |
0.0000000 |
0.0000000 |
0.0144867 |
| BRAIN_BODY |
0.0000000 |
0.0000397 |
0.0414927 |
0.7322638 |
0.1188045 |
0.0241044 |
0.0144867 |
0.0000000 |

Видим, что для всех пар признаков критерий даёт p-value меньше \(\alpha = 0.05\), то есть отвергаем гипотезу
о незначимости зависимсоти для этих признаков.
Зависимости:
Вес мозга зависит от веса
тела;
Общая продолжительность сна зависит от
медленного и быстрого сна, а также они
зависят друг от друга, но скорее косвенно, так как это соотношение
скорее индивидуально для каждого животного;
Время беременности зависит от
продолжительности жизни, так как беременность не должна
занимать большей части жизни из-за уязвимости такого животного;
Чем больше животное, тем дольше беременность и
продолжительность жизни, так как крупное животное
требует как большого формирования во время беременности, так как и
большего времени взросления;
Также от размера мозга зависит
продолжительность жизни, однако эта зависимость не
является прямой, так как если мы рассмотрим долю веса
мозга к весу тела, то корреляция значительно
упадёт;
Зависимости между сном и весом
тела, сном и продолжительностью
жизни или сном и времени
беременности достаточно сложно объяснить, для этого нужно
углублятся в сложные биологичесике процессы.
11. Коэффициент Спирмена
Проверим, что ранговый коэффициент корреляции Спирмена примерно равен
Пирсону:
df.log.out |>
dplyr::select(-NAME, -EXP_IND, -PRED_IND, -DANG_IND) |>
pairwise.cor(name_method = "spearman") |>
print_df()
| |
BODY_WEI |
BRAIN_WE |
SLOWWAVE |
PARADOX |
SLEEP |
LIFESPAN |
GESTTIME |
| BODY_WEI |
0.0000000 |
0.0000000 |
0.0019593 |
0.0063690 |
0.0004095 |
0.0000000 |
0.00e+00 |
| BRAIN_WE |
0.0000000 |
0.0000000 |
0.0008980 |
0.0004787 |
0.0000531 |
0.0000000 |
0.00e+00 |
| SLOWWAVE |
0.0019593 |
0.0008980 |
0.0000000 |
0.0000730 |
0.0000000 |
0.0010380 |
1.59e-05 |
| PARADOX |
0.0063690 |
0.0004787 |
0.0000730 |
0.0000000 |
0.0000000 |
0.0026538 |
2.60e-06 |
| SLEEP |
0.0004095 |
0.0000531 |
0.0000000 |
0.0000000 |
0.0000000 |
0.0000883 |
1.00e-07 |
| LIFESPAN |
0.0000000 |
0.0000000 |
0.0010380 |
0.0026538 |
0.0000883 |
0.0000000 |
0.00e+00 |
| GESTTIME |
0.0000000 |
0.0000000 |
0.0000159 |
0.0000026 |
0.0000001 |
0.0000000 |
0.00e+00 |

Результаты схожи и это не удивительно, ведь мы считаем, что данные
похожи на нормальные, а для них отличие коэффициента Пирсона от Спирмена
незначительно.
12. Частные корреляционные отношения
Однако возникает подозрение, что многие зависимости имеют другую
причину, нежели зависимость только друг от друга. Как было видно по
ящикам с усами для индекса опасности места для сна есть
монотонная зависимость почти всех признаков от этого индекса. Поэтому
возникает идея проверки коэффициента частной корреляции с вычетом
влияния этого индекса.
df.log.out |>
group_by(EXP_IND) |>
mutate(BODY_WEI = BODY_WEI - mean(BODY_WEI, na.rm = TRUE), BRAIN_WE = BRAIN_WE - mean(BRAIN_WE, na.rm = TRUE), SLOWWAVE = SLOWWAVE - mean(SLOWWAVE, na.rm = TRUE), PARADOX = PARADOX - mean(PARADOX, na.rm = TRUE), SLEEP = SLEEP - mean(SLEEP, na.rm = TRUE), LIFESPAN = LIFESPAN - mean(LIFESPAN, na.rm = TRUE), GESTTIME = GESTTIME - mean(GESTTIME, na.rm = TRUE)) |>
group_by(.drop = TRUE) |> dplyr::select(-NAME, -EXP_IND, -PRED_IND, -DANG_IND) |>
pairwise.cor() |>
print_df()
| |
BODY_WEI |
BRAIN_WE |
SLOWWAVE |
PARADOX |
SLEEP |
LIFESPAN |
GESTTIME |
| BODY_WEI |
0.0000000 |
0.0000000 |
0.1044414 |
0.3927193 |
0.5207584 |
0.0000000 |
0.0000003 |
| BRAIN_WE |
0.0000000 |
0.0000000 |
0.0715185 |
0.9971036 |
0.1841235 |
0.0000000 |
0.0000000 |
| SLOWWAVE |
0.1044414 |
0.0715185 |
0.0000000 |
0.0055510 |
0.0000000 |
0.0723565 |
0.0053589 |
| PARADOX |
0.3927193 |
0.9971036 |
0.0055510 |
0.0000000 |
0.0000016 |
0.7878256 |
0.0094343 |
| SLEEP |
0.5207584 |
0.1841235 |
0.0000000 |
0.0000016 |
0.0000000 |
0.0607429 |
0.0002778 |
| LIFESPAN |
0.0000000 |
0.0000000 |
0.0723565 |
0.7878256 |
0.0607429 |
0.0000000 |
0.0000633 |
| GESTTIME |
0.0000003 |
0.0000000 |
0.0053589 |
0.0094343 |
0.0002778 |
0.0000633 |
0.0000000 |

Здесь мы видим, что достаточно много зависимостей перестали быть
значимыми (\(\alpha = 0.05\)), так как
мы исключили влияние индекса опасности места для сна.
Ожидаемо зависимости всех видов сна от веса
тела, веса мозга и продолжительности
сна значительно ослабли, однако зависимости сна от
времени беременности уменьшились мало.
13. Множественная регрессия
Посмотрим на данные:
df.log.out |> dplyr::select(-NAME) |> ggpairs(columns = 1:7)

Всё, что нужно, было прологарифмировано раньше, поэтому приступим к
регрессии. Хотим прогнозировать время жизни по остальным данным.
Строим регрессию по всем признакам:
df.log.out.na <- df.log.out |> dplyr::filter(!is.na(SLOWWAVE) & !is.na(PARADOX) & !is.na(SLEEP) & !is.na(LIFESPAN) & !is.na(GESTTIME))
model <- lm(LIFESPAN ~ ., df.log.out.na |> dplyr::select(-NAME))
model.beta <- lm.beta(model)
summary(model.beta)
Call:
lm(formula = LIFESPAN ~ ., data = dplyr::select(df.log.out.na,
-NAME))
Residuals:
Min 1Q Median 3Q Max
-0.77116 -0.16275 0.02196 0.12238 0.51269
Coefficients:
Estimate Standardized Std. Error t value Pr(>|t|)
(Intercept) 1.348343 NA 0.903753 1.492 0.151321
BODY_WEI -0.022443 -0.065240 0.096158 -0.233 0.817825
BRAIN_WE 0.461812 1.096994 0.104491 4.420 0.000264 ***
SLOWWAVE 0.095508 0.327255 0.176317 0.542 0.594021
PARADOX 0.121882 0.080915 0.338311 0.360 0.722426
SLEEP -0.050191 -0.215930 0.181925 -0.276 0.785466
GESTTIME -0.099790 -0.105443 0.159882 -0.624 0.539586
PRED_IND2 -0.882934 -0.383209 0.482871 -1.829 0.082427 .
PRED_IND3 -1.451831 -0.553794 0.624294 -2.326 0.030675 *
PRED_IND4 -1.151949 -0.439406 0.754374 -1.527 0.142415
PRED_IND5 -1.098412 -0.459997 0.796382 -1.379 0.183047
EXP_IND2 0.069167 0.027761 0.224152 0.309 0.760836
EXP_IND3 0.027348 0.008247 0.293812 0.093 0.926767
EXP_IND4 -0.385732 -0.116321 0.421313 -0.916 0.370812
EXP_IND5 0.003063 0.001169 0.555897 0.006 0.995658
DANG_IND2 0.703874 0.294771 0.457148 1.540 0.139305
DANG_IND3 1.044139 0.398282 0.624193 1.673 0.109941
DANG_IND4 1.275729 0.534255 0.811954 1.571 0.131827
DANG_IND5 1.162274 0.386226 1.122186 1.036 0.312693
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.3471 on 20 degrees of freedom
Multiple R-squared: 0.939, Adjusted R-squared: 0.884
F-statistic: 17.09 on 18 and 20 DF, p-value: 1.994e-08
Регрессия значима, но значимых коэффициентов на уровне \(\alpha = 0.05\) только два: для
веса мозга и для фиктивной переменной индекса
хищничества при значении \(3\).
14. Доверительные эллипсы
Посчитаем ковариационную и корреляционную матрицу для коэффициентов
регрессии:
dummy.variable <- data.frame(NAME = df.log.out.na$NAME)
for (i in 2:5){
dummy.variable[[paste("PRED_IND", i, sep = "_")]] = ifelse(df.log.out.na$PRED_IND == i, 1, 0)
}
for (i in 2:5){
dummy.variable[[paste("EXP_IND", i, sep = "_")]] = ifelse(df.log.out.na$EXP_IND == i, 1, 0)
}
for (i in 2:5){
dummy.variable[[paste("DANG_IND", i, sep = "_")]] = ifelse(df.log.out.na$DANG_IND == i, 1, 0)
}
n.df <- length(df.log.out.na$NAME)
sigma.df <- sum(model$residuals ** 2) / (n.df - model$rank)
covMatrix.df <- df.log.out.na |> dplyr::select(-NAME, -PRED_IND, -EXP_IND, -DANG_IND) |> cbind(dummy.variable |> dplyr::select(-NAME)) |> cov()
cov_b.df <- sigma.df / n.df * solve(covMatrix.df)
cov_beta.df <- sigma.df / n.df * solve(cov2cor(covMatrix.df))
cor_b.df <- cov2cor(cov_b.df)
corrplot(cor_b.df, method = "color")

Видим сильную корреляцию между между оценками коэффициентов регрессии
между весом тела и мозга, между всеми
видами сна, между индексами общей
опасности и хищничества, а также между
весом мозга и продолжительностью
жизни.
Посмотрим на доверительный эллипсоид для значимых коэффициентов,
обозначенных выше:
plot(ellipse::ellipse(cov_beta.df[c(2, 9), c(2, 9)], centre = model.beta$standardized.coefficients[c(3, 9)], level=0.95, npoints = 100), type = "l", asp = 1)
lines(x = c(-2, 2), y = c(2, -2), col = "red")

Он имеет плохой вид: либо оба имеет сильное влияние, либо оба —
слабое. Причём индекс хищничества может быть совсем не
значим, так как эллипс пересекает нулевое значение по \(y\).
15. Сильно коррелирующие признаки
Построим таблицу VIF:
ols_vif_tol(model)
Наблюдаем очень большую мультиколлинеарность почти по всем
признакам.
Теперь таблица с частичными корреляциями:
ols_correlations(model)
Correlations
--------------------------------------------
Variable Zero Order Partial Part
--------------------------------------------
BODY_WEI 0.842 -0.052 -0.013
BRAIN_WE 0.933 0.703 0.244
SLOWWAVE -0.497 0.120 0.030
PARADOX -0.369 0.080 0.020
SLEEP -0.517 -0.062 -0.015
GESTTIME 0.737 -0.138 -0.034
PRED_IND2 -0.209 -0.378 -0.101
PRED_IND3 -0.241 -0.461 -0.128
PRED_IND4 0.075 -0.323 -0.084
PRED_IND5 0.090 -0.295 -0.076
EXP_IND2 -0.243 0.069 0.017
EXP_IND3 0.220 0.021 0.005
EXP_IND4 0.158 -0.201 -0.051
EXP_IND5 0.455 0.001 0.000
DANG_IND2 -0.215 0.326 0.085
DANG_IND3 -0.399 0.350 0.092
DANG_IND4 0.218 0.331 0.087
DANG_IND5 0.307 0.226 0.057
--------------------------------------------
Сильное влияние на зависимую переменную за исключением остальных
оказывают, в перую очередь, вес мозга, а также
индекс хищничества и общий индекс
опасности (хотя он сильно зависит от индекса
хищничества).
Попробуем убрать сильно коррелирующие. Уберём вес
тела, общее время сна и общий индекс
опасности:
model.minuscor <- lm(LIFESPAN ~ ., df.log.out.na |> dplyr::select(-NAME, -BODY_WEI, - SLEEP, -DANG_IND))
model.minuscor.beta <- lm.beta(model.minuscor)
summary(model.minuscor.beta)
Call:
lm(formula = LIFESPAN ~ ., data = dplyr::select(df.log.out.na,
-NAME, -BODY_WEI, -SLEEP, -DANG_IND))
Residuals:
Min 1Q Median 3Q Max
-0.70225 -0.16574 0.03341 0.20078 0.47870
Coefficients:
Estimate Standardized Std. Error t value Pr(>|t|)
(Intercept) 1.552951 NA 0.629838 2.466 0.0206 *
BRAIN_WE 0.427375 1.015193 0.061132 6.991 2.01e-07 ***
SLOWWAVE 0.023400 0.080180 0.029182 0.802 0.4299
PARADOX -0.079520 -0.052792 0.153749 -0.517 0.6094
GESTTIME -0.087863 -0.092841 0.130827 -0.672 0.5078
PRED_IND2 -0.371255 -0.161131 0.227439 -1.632 0.1147
PRED_IND3 -0.534832 -0.204009 0.240393 -2.225 0.0350 *
PRED_IND4 -0.062608 -0.023882 0.326654 -0.192 0.8495
PRED_IND5 -0.053963 -0.022599 0.345540 -0.156 0.8771
EXP_IND2 0.206412 0.082846 0.184211 1.121 0.2727
EXP_IND3 0.121182 0.036543 0.258597 0.469 0.6433
EXP_IND4 -0.189088 -0.057021 0.323711 -0.584 0.5642
EXP_IND5 -0.020639 -0.007873 0.357456 -0.058 0.9544
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.3648 on 26 degrees of freedom
Multiple R-squared: 0.9123, Adjusted R-squared: 0.8719
F-statistic: 22.55 on 12 and 26 DF, p-value: 1.012e-10
По значимости имеем ту же ситуацию, однако значимость по p-value
подросла, а \(R^2\) почти не
изменился.
Мультиколлинеарность исчезла:
ols_vif_tol(model.minuscor)
16. Пошаговая регрессия
Backward
Построим пошаговую регрессию. Сначала рассмотрим убавление количества
параметров:
st.bw.lm <- ols_step_backward_p(model.minuscor)
st.bw.lm
Stepwise Summary
-----------------------------------------------------------------------
Step Variable AIC SBC SBIC R2 Adj. R2
-----------------------------------------------------------------------
0 Full Model 44.212 67.502 -71.965 0.91234 0.87188
1 PARADOX 42.612 64.238 -74.719 0.91144 0.87536
2 GESTTIME 40.951 60.914 -77.453 0.91066 0.87875
3 EXP_IND 35.968 49.276 -78.073 0.90348 0.88538
4 SLOWWAVE 34.481 46.126 -80.367 0.90220 0.88738
-----------------------------------------------------------------------
Final Model Output
------------------
Model Summary
--------------------------------------------------------------
R 0.950 RMSE 0.315
R-Squared 0.902 MSE 0.117
Adj. R-Squared 0.887 Coef. Var 14.153
Pred R-Squared 0.864 AIC 34.481
MAE 0.260 SBC 46.126
--------------------------------------------------------------
RMSE: Root Mean Square Error
MSE: Mean Square Error
MAE: Mean Absolute Error
AIC: Akaike Information Criteria
SBC: Schwarz Bayesian Criteria
ANOVA
-------------------------------------------------------------------
Sum of
Squares DF Mean Square F Sig.
-------------------------------------------------------------------
Regression 35.614 5 7.123 60.884 0.0000
Residual 3.861 33 0.117
Total 39.475 38
-------------------------------------------------------------------
Parameter Estimates
----------------------------------------------------------------------------------------
model Beta Std. Error Std. Beta t Sig lower upper
----------------------------------------------------------------------------------------
(Intercept) 1.534 0.175 8.760 0.000 1.178 1.890
BRAIN_WE 0.375 0.024 0.890 15.606 0.000 0.326 0.424
PRED_IND2 -0.311 0.184 -0.135 -1.686 0.101 -0.685 0.064
PRED_IND3 -0.569 0.196 -0.217 -2.909 0.006 -0.968 -0.171
PRED_IND4 -0.150 0.193 -0.057 -0.777 0.443 -0.541 0.242
PRED_IND5 -0.114 0.183 -0.048 -0.625 0.536 -0.487 0.258
----------------------------------------------------------------------------------------
Как это выглядит по шагам:
plot(st.bw.lm)


Какая же модель получается в итоге:
summary(st.bw.lm$model)
Call:
lm(formula = paste(response, "~", paste(c(include, cterms), collapse = " + ")),
data = l)
Residuals:
Min 1Q Median 3Q Max
-0.7207 -0.2307 -0.0212 0.2462 0.5830
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 1.53397 0.17511 8.760 3.99e-10 ***
BRAIN_WE 0.37477 0.02401 15.606 < 2e-16 ***
PRED_IND2 -0.31054 0.18418 -1.686 0.10121
PRED_IND3 -0.56931 0.19574 -2.909 0.00645 **
PRED_IND4 -0.14951 0.19252 -0.777 0.44291
PRED_IND5 -0.11438 0.18295 -0.625 0.53612
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.342 on 33 degrees of freedom
Multiple R-squared: 0.9022, Adjusted R-squared: 0.8874
F-statistic: 60.88 on 5 and 33 DF, p-value: 1.066e-15
Осталось \(2\) признака, удалились
время беременности, быстрый сон,
медленный сон и индекс воздействия
сна.
Forward
Теперь будем добавлять признаки:
st.fw.lm <- ols_step_forward_p(model.minuscor)
st.fw.lm
Stepwise Summary
-------------------------------------------------------------------------
Step Variable AIC SBC SBIC R2 Adj. R2
-------------------------------------------------------------------------
0 Base Model 115.149 118.476 1.181 0.00000 0.00000
1 BRAIN_WE 37.664 42.655 -73.233 0.86972 0.86620
2 PRED_IND 34.481 46.126 -80.767 0.90220 0.88738
-------------------------------------------------------------------------
Final Model Output
------------------
Model Summary
--------------------------------------------------------------
R 0.950 RMSE 0.315
R-Squared 0.902 MSE 0.117
Adj. R-Squared 0.887 Coef. Var 14.153
Pred R-Squared 0.864 AIC 34.481
MAE 0.260 SBC 46.126
--------------------------------------------------------------
RMSE: Root Mean Square Error
MSE: Mean Square Error
MAE: Mean Absolute Error
AIC: Akaike Information Criteria
SBC: Schwarz Bayesian Criteria
ANOVA
-------------------------------------------------------------------
Sum of
Squares DF Mean Square F Sig.
-------------------------------------------------------------------
Regression 35.614 5 7.123 60.884 0.0000
Residual 3.861 33 0.117
Total 39.475 38
-------------------------------------------------------------------
Parameter Estimates
----------------------------------------------------------------------------------------
model Beta Std. Error Std. Beta t Sig lower upper
----------------------------------------------------------------------------------------
(Intercept) 1.534 0.175 8.760 0.000 1.178 1.890
BRAIN_WE 0.375 0.024 0.890 15.606 0.000 0.326 0.424
PRED_IND2 -0.311 0.184 -0.135 -1.686 0.101 -0.685 0.064
PRED_IND3 -0.569 0.196 -0.217 -2.909 0.006 -0.968 -0.171
PRED_IND4 -0.150 0.193 -0.057 -0.777 0.443 -0.541 0.242
PRED_IND5 -0.114 0.183 -0.048 -0.625 0.536 -0.487 0.258
----------------------------------------------------------------------------------------
Как это выглядело на графике:
plot(st.fw.lm)


Какую же модель получили:
summary(st.fw.lm$model)
Call:
lm(formula = paste(response, "~", paste(preds, collapse = " + ")),
data = l)
Residuals:
Min 1Q Median 3Q Max
-0.7207 -0.2307 -0.0212 0.2462 0.5830
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 1.53397 0.17511 8.760 3.99e-10 ***
BRAIN_WE 0.37477 0.02401 15.606 < 2e-16 ***
PRED_IND2 -0.31054 0.18418 -1.686 0.10121
PRED_IND3 -0.56931 0.19574 -2.909 0.00645 **
PRED_IND4 -0.14951 0.19252 -0.777 0.44291
PRED_IND5 -0.11438 0.18295 -0.625 0.53612
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.342 on 33 degrees of freedom
Multiple R-squared: 0.9022, Adjusted R-squared: 0.8874
F-statistic: 60.88 on 5 and 33 DF, p-value: 1.066e-15
Она аналогична той, которую получили от backward.
Однако только при одном значении индекса хищничества
коэффициент значимый, поэтому вручную создадим фиктивные переменные и
проведём опять автоматический отбор:
df.log.out.dummy <- df.log.out.na
for (i in 2:5){
df.log.out.dummy[[paste("PRED_IND", i, sep = "_")]] <- ifelse(df.log.out.dummy$PRED_IND == i, 1, 0)
}
model.out.dummy <- lm(LIFESPAN ~ BRAIN_WE + PRED_IND_2 + PRED_IND_3 + PRED_IND_4 + PRED_IND_5, data = df.log.out.dummy)
st.bw.lm.dummy <- ols_step_backward_p(model.out.dummy)
plot(st.bw.lm.dummy)


summary(st.bw.lm.dummy$model)
Call:
lm(formula = paste(response, "~", paste(c(include, cterms), collapse = " + ")),
data = l)
Residuals:
Min 1Q Median 3Q Max
-0.63197 -0.23515 -0.04439 0.22367 0.67817
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 1.42794 0.10776 13.251 3.35e-15 ***
BRAIN_WE 0.37812 0.02315 16.332 < 2e-16 ***
PRED_IND_2 -0.21197 0.13117 -1.616 0.11507
PRED_IND_3 -0.47162 0.14733 -3.201 0.00291 **
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.3354 on 35 degrees of freedom
Multiple R-squared: 0.9003, Adjusted R-squared: 0.8917
F-statistic: 105.3 on 3 and 35 DF, p-value: < 2.2e-16
Осталось два значения индекса хищничества.
17. Остатки и Predicted vs Residuals
Нашли лучшую модель регрессии. Добавим те наблюдения, которые имели
пропущенные значения только на убранных данных:
df.log.na.best <- df.log |> filter(!is.na(LIFESPAN))
for (i in 2:5){
df.log.na.best[[paste("PRED_IND", i, sep = "_")]] <- ifelse(df.log.na.best$PRED_IND == i, 1, 0)
}
model.best <- lm(LIFESPAN ~ BRAIN_WE + PRED_IND_2 + PRED_IND_3, data = df.log.na.best)
model.beta.best <- lm.beta(model.best)
summary(model.beta.best)
Call:
lm(formula = LIFESPAN ~ BRAIN_WE + PRED_IND_2 + PRED_IND_3, data = df.log.na.best)
Residuals:
Min 1Q Median 3Q Max
-1.12708 -0.35389 -0.02041 0.23742 1.77925
Coefficients:
Estimate Standardized Std. Error t value Pr(>|t|)
(Intercept) 1.80029 NA 0.14546 12.377 < 2e-16 ***
BRAIN_WE 0.28962 0.76276 0.03015 9.605 2.79e-13 ***
PRED_IND_2 -0.22525 -0.10042 0.18422 -1.223 0.22676
PRED_IND_3 -0.52686 -0.22082 0.19306 -2.729 0.00856 **
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.5536 on 54 degrees of freedom
Multiple R-squared: 0.6738, Adjusted R-squared: 0.6557
F-statistic: 37.19 on 3 and 54 DF, p-value: 3.589e-13
Ясно, что согласованность модели упала, так как мы построили её на
большем наборе данных (плюс были добавлены выбросы, удаленные
раньше).
Теперь посмотрим на нормальность остатков (хотим точность проверяемых
критериев):
plot(model.best, which=2)

Видим, что справа нормальности не наблюдается, хотя на основном
массиве данных нормальность присутствует.
Теперь посмотрим на график Predicted vs Residuals:
plot(model.best,which=1)

Видим достаточно равномерное распределение в прямоугольной области
около нуля. То есть можно предположить адекватность выбранной линейной
модели, а также гомогедостичность (то есть равенство дисперсий
остатков).
График Residuls vs Deleted Residuals:
del_res <- sapply(1:58, function(n) df.log.na.best$LIFESPAN[n] - predict(lm(LIFESPAN ~ BRAIN_WE + PRED_IND_2 + PRED_IND_3, data = df.log.na.best[-n, ]), df.log.na.best[n, ]))
plot(x = model.best$residuals, y = del_res, xlab = "Rsiduals", ylab = "Deleted residuals")
lines(x = c(-3, 3), y = c(-3, 3))
abline(lm(del_res ~ model.best$residuals), col = "blue")
text(x = model.best$residuals, y = del_res, labels = rownames(df.log.na.best), cex = 0.6, pos = 4, col = "red")

Как и ожидалось, точки расположились под чуть большим наклоном,
нежели \(y = x\). Больших отклонений
(по линии регресии на этих точках) не видно, то есть сказать что-то о
выбросах нельзя.
18. Выбросы по Махаланобису
df.log.na.best.x <- df.log.na.best |> dplyr::select(BRAIN_WE, PRED_IND_2, PRED_IND_3)
cov_matrix <- cov(df.log.na.best.x)
mahalanobis_distance <- mahalanobis(df.log.na.best.x, center = colMeans(df.log.na.best.x), cov = cov_matrix)
plot(mahalanobis_distance,
main = "Mahalanobis Distance Plot",
xlab = "Observation",
ylab = "Mahalanobis Distance",
type = "h",
col = "darkblue")

plot(sort(mahalanobis_distance),
main = "Mahalanobis Distance Plot",
xlab = "Observation",
ylab = "Mahalanobis Distance",
type = "h",
col = "darkblue")

По Махаланобису виден скачок для двух наблюдений: \(1\) и \(4\). Это два слона:
| NAME |
BODY_WEI |
BRAIN_WE |
SLOWWAVE |
PARADOX |
SLEEP |
LIFESPAN |
GESTTIME |
PRED_IND |
EXP_IND |
DANG_IND |
PRED_IND_2 |
PRED_IND_3 |
PRED_IND_4 |
PRED_IND_5 |
| African elephant |
8.802974 |
8.650324 |
NA |
NA |
3.3 |
3.653252 |
6.46925 |
3 |
5 |
3 |
0 |
1 |
0 |
0 |
| Asian elephant |
7.842671 |
8.434463 |
2.1 |
0.5877867 |
3.9 |
4.234107 |
6.43615 |
3 |
5 |
4 |
0 |
1 |
0 |
0 |
19. Выбросы по Куку
cooks_distance <- cooks.distance(model.best)
plot(cooks_distance,
col = "darkblue",
type = "h",
main = "Cook's Distance",
xlab = "Observation",
ylab = "Cook's Distance")

plot(sort(cooks_distance),
col = "darkblue",
type = "h",
main = "Cook's Distance",
xlab = "Observation",
ylab = "Cook's Distance")

По скачку значения расстояния Кука можно выделить три наблюдения: 6,
14 и 31. Это те самые летучие мыши и ещё ехидна:
df.log.na.best[c(6, 14, 31), ] |> print_df()
| NAME |
BODY_WEI |
BRAIN_WE |
SLOWWAVE |
PARADOX |
SLEEP |
LIFESPAN |
GESTTIME |
PRED_IND |
EXP_IND |
DANG_IND |
PRED_IND_2 |
PRED_IND_3 |
PRED_IND_4 |
PRED_IND_5 |
| Big brown bat |
-3.772261 |
-1.203973 |
15.8 |
1.3609766 |
19.7 |
2.944439 |
3.555348 |
1 |
1 |
1 |
0 |
0 |
0 |
0 |
| Echidna |
1.098612 |
3.218876 |
8.6 |
NA |
8.6 |
3.912023 |
3.332205 |
2 |
2 |
2 |
1 |
0 |
0 |
0 |
| Little brown bat |
-4.605170 |
-1.386294 |
17.9 |
0.6931472 |
19.9 |
3.178054 |
3.912023 |
1 |
1 |
1 |
0 |
0 |
0 |
0 |
20. Studentized Residuals vs Leverage Plot
Наконец посмотрим на соотношение рычага и остатков:
ols_plot_resid_lev(model.best)

Здесь явных выбросов нет: по остаткам, за пределами доверительного
интервала небольшое количество наблюдений, по рычагу два кандидата на
выбросы — слоны, а наблюдений, которые бы были и по рычагу и по остаткам
“выбросами”, нет.
21. Исключаем выбросы
Найденные выбросы имеют определённый смысл — большие и средне
атакуемые слоны, маленькие, но долгоживущие летучие мыши и такая же
ехидна. Слонов исключать не будем, так как они просто большие, поэтому
Махаланобис их выделяет, однако они хорошо согласуются с моделью (видно
по картинке, например). Исключим выбросы:
model.best.out <- lm(LIFESPAN ~ BRAIN_WE + PRED_IND_2 + PRED_IND_3, data = df.log.na.best[c(-6, -14, -31),])
model.best.out.beta <- lm.beta(model.best.out)
summary(model.best.out.beta)
Call:
lm(formula = LIFESPAN ~ BRAIN_WE + PRED_IND_2 + PRED_IND_3, data = df.log.na.best[c(-6,
-14, -31), ])
Residuals:
Min 1Q Median 3Q Max
-1.0491 -0.2596 0.0245 0.2405 1.0616
Coefficients:
Estimate Standardized Std. Error t value Pr(>|t|)
(Intercept) 1.47436 NA 0.11487 12.835 <2e-16 ***
BRAIN_WE 0.34604 0.87394 0.02316 14.941 <2e-16 ***
PRED_IND_2 -0.15023 -0.06611 0.13724 -1.095 0.279
PRED_IND_3 -0.36992 -0.15766 0.13833 -2.674 0.010 *
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.3905 on 51 degrees of freedom
Multiple R-squared: 0.8394, Adjusted R-squared: 0.83
F-statistic: 88.87 on 3 and 51 DF, p-value: < 2.2e-16
Судя по \(R^2\) и его исправленному
варианту, а также по p-value значимости регрессии, получили более
хорошую модель.
22. Предсказания
Предскажем время жизниобыкновенного ежа. Вес
его мозга — \(3.3\) г, имеет
защиту от хищников, но крупные хищные птицы успешно их атакуют, поэтому
индекс хищничества — примерно \(3\). В неволе ежи живут \(4\)-\(6\)
лет. Пусть будет \(5\):
data.hedgehog <- data.frame(NAME = "European hedgehog", BRAIN_WE = log(3.3), LIFESPAN = log(5), PRED_IND_2 = 0, PRED_IND_3 = 1)
data.hedgehog |> print_df()
| NAME |
BRAIN_WE |
LIFESPAN |
PRED_IND_2 |
PRED_IND_3 |
| European hedgehog |
1.193922 |
1.609438 |
0 |
1 |
Предскажем его продолжительность жизни:
pred.hedgehog.pred <- predict(model.best.out, newdata = data.hedgehog, interval = "predict")
pred.hedgehog.conf <- predict(model.best.out, newdata = data.hedgehog, interval = "confidence")
exp(pred.hedgehog.pred[, 1])
[1] 4.561225
Получили цифры близкие к действительности. Посмотрим на
предсказательный интервал:
print("Нижняя граница:")
[1] "Нижняя граница:"
exp(pred.hedgehog.pred[, 2])
[1] 2.002536
print("Верхняя граница:")
[1] "Верхняя граница:"
exp(pred.hedgehog.pred[, 3])
[1] 10.38922
Ежи в неволе могут жить до 10 лет.
А теперь на доверительный:
print("Нижняя граница:")
[1] "Нижняя граница:"
exp(pred.hedgehog.conf[, 2])
[1] 3.549484
print("Верхняя граница:")
[1] "Верхняя граница:"
exp(pred.hedgehog.conf[, 3])
[1] 5.861353
LS0tCnRpdGxlOiAiU2xlZXAiCmF1dGhvcjogIk9sZXluaWsgTWljaGFlbCIKZGF0ZTogIjIwMjMtMTEtMjAiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMjIDAuINCR0LjQsdC70LjQvtGC0LXQutC4INC4INC/0YDQtdC00LLQsNGA0LjRgtC10LvRjNC90LDRjyDQv9C+0LTQs9C+0YLQvtCy0LrQsAoKYGBge3IgbWVzc2FnZT1GQUxTRX0KbGlicmFyeShyZWFkeGwpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoc2NhdHRlclBsb3RNYXRyaXgpCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShwc3ljaCkKbGlicmFyeShzdW1tYXJ5dG9vbHMpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShHR2FsbHkpCmxpYnJhcnkobm9ydGVzdCkKbGlicmFyeShtb21lbnRzKQpsaWJyYXJ5KHBwY29yKQpsaWJyYXJ5KGNvcnJwbG90KQpsaWJyYXJ5KGxtLmJldGEpCmxpYnJhcnkoZWxsaXBzZSkKbGlicmFyeShjYXIpCmxpYnJhcnkob2xzcnIpCnByaW50X2RmIDwtIGZ1bmN0aW9uKGRmKQp7CiAgZGYgfD4KICAgIGthYmxlKGZvcm1hdCA9ICJodG1sIikgfD4KICAgIGthYmxlX3N0eWxpbmcoKSB8PgogICAga2FibGVFeHRyYTo6c2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIiwgaGVpZ2h0ID0gIjEwMCUiKQp9CgpwbG90X3FxX2dyYXBoIDwtIGZ1bmN0aW9uKGRhdGEsIGNvbHVtbl9uYW1lKSB7CiAgZGF0YTwtZGF0YQogIGV4cGVjdGVkX3F1YW50aWxlcyA8LSBxbm9ybShwcG9pbnRzKGxlbmd0aChkYXRhKSkpCiAgcXFub3JtKGRhdGEsICBwY2ggPSAxOSwgY29sID0gImRlZXBwaW5rIikKICBxcWxpbmUoZGF0YSwgZGlzdHJpYnV0aW9uID0gcW5vcm0sIGx3ZCA9IDIsIGNvbCA9ICJsaW1lZ3JlZW4iKQogIGxlZ2VuZCgidG9wbGVmdCIsIGxlZ2VuZCA9IGNvbHVtbl9uYW1lLCBjb2wgPSAiZGVlcHBpbmsiLCBwY2ggPSAxOSkKfQoKcGxvdF9wcF9wbG90IDwtIGZ1bmN0aW9uKGRhdGEsIGNvbHVtbl9uYW1lKSB7CiAgbiA8LSBsZW5ndGgoZGF0YSkKICBleHBlY3RfcHJvYiA8LSBwbm9ybShkYXRhLCBtZWFuKGRhdGEpLCBzcXJ0KHZhcihkYXRhKSAqIChuIC0gMSkgLyBuKSkKICBleHBlY3RfcHJvYiA8LSBzb3J0KGV4cGVjdF9wcm9iKQogIHBsb3QoeCA9IGV4cGVjdF9wcm9iLCB5ID0gcHBvaW50cyhuKSwgY29sID0gInJlZCIpCiAgbGluZXMoeCA9IGMoMCwgMSksIHkgPSBjKDAsIDEpLCBjb2wgPSAiYmx1ZSIpCiAgbGVnZW5kKCJ0b3BsZWZ0IiwgbGVnZW5kID0gY29sdW1uX25hbWUsIGNvbCA9ICJkZWVwcGluayIsIHBjaCA9IDE5KQp9CmBgYAoKIyMgMS4g0JfQsNCz0YDRg9C30LrQsCDQuCDQv9GA0LXQtNC+0LHRgNCw0LHQvtGC0LrQsCDQtNCw0L3QvdGL0YUKCmBgYHtyfQpkZiA8LSByZWFkX2V4Y2VsKCJzdGF0MjAyMV9zbS9TbGVlcC9TTEVFUF9zaG9ydG5hbWUueGxzIiwgKQpkZiA8LSBkZiB8PiBtdXRhdGUoU0xFRVAgPSBpZmVsc2UoU0xFRVAgPCAwLCBOQSwgU0xFRVApLCBQQVJBRE9YID0gaWZlbHNlKGlzLmluZmluaXRlKFBBUkFET1gpLCBOQSwgUEFSQURPWCksIFBSRURfSU5EID0gYXMuZmFjdG9yKFBSRURfSU5EKSwgRVhQX0lORCA9IGFzLmZhY3RvcihFWFBfSU5EKSwgREFOR19JTkQgPSBhcy5mYWN0b3IoREFOR19JTkQpKSAKZGYgfD4gc2xpY2UoMToxMCkgfD4gcHJpbnRfZGYoKQpgYGAKCiMjIDIuINCe0L/QuNGB0LDQvdC40LUg0L/RgNC40LfQvdCw0LrQvtCyCgoxLiAqKipOQU1FKioqIC0tLSDQvdCw0LfQstCw0L3QuNC1INC20LjQstC+0YLQvdC+0LPQviAtLS0gKirQutCw0YfQtdGB0YLQstC10L3QvdGL0Lkg0L/RgNC40LfQvdCw0LoqKgoyLiAqKipCT0RZX1dFSSoqKiAtLS0g0LLQtdGBINGC0LXQu9CwINCyINC60LjQu9C+0LPRgNCw0LzQvNCw0YUgLS0tICoq0LrQvtC70LjRh9C10YHRgtCy0LXQvdC90Ysg0L3QtdC/0YDQtdGA0YvQstC90YvQuSDQv9GA0LjQt9C90LDQuioqCjMuICoqKkJSQUlOX1dFCSoqKiAtLS0g0LLQtdGBINC80L7Qt9Cz0LAg0LIg0LPRgNCw0LzQvNCw0YUgLS0tICoq0LrQvtC70LjRh9C10YHRgtCy0LXQvdC90YvQuSDQvdC10L/RgNC10YDRi9Cy0L3Ri9C5INC/0YDQuNC30L3QsNC6KioKNC4gKioqU0xPV1dBVkUqKiogLS0tINCy0YDQtdC80Y8g0LzQtdC00LvQtdC90L3QvtCz0L4g0YHQvdCwLCDRh9Cw0YHQvtCyINCyINC00LXQvdGMIC0tLSAqKtC60L7Qu9C40YfQtdGB0YLQstC10L3QvdGL0Lkg0L3QtdC/0YDQtdGA0YvQstC90YvQuSDQv9GA0LjQt9C90LDQuioqCjUuICoqKlBBUkFET1gqKiogLS0t0LLRgNC10LzRjyDQsdGL0YHRgtGA0L7Qs9C+INGB0L3QsCwg0YfQsNGB0L7QsiDQsiDQtNC10L3RjCAgLS0tICoq0LrQvtC70LjRh9C10YHRgtCy0LXQvdC90YvQuSDQvdC10L/RgNC10YDRi9Cy0L3Ri9C5INC/0YDQuNC30L3QsNC6KioKNi4gKioqU0xFRVAqKiogLS0tINC+0LHRidC10LUg0LLRgNC10LzRjyDRgdC90LAsINGH0LDRgdC+0LIg0LIg0LTQtdC90YwgLS0tICoq0LrQvtC70LjRh9C10YHRgtCy0LXQvdC90YvQuSDQvdC10L/RgNC10YDRi9Cy0L3Ri9C5INC/0YDQuNC30L3QsNC6KioKNy4gKioqTElGRVNQQU4qKiogLS0tINC/0YDQvtC00L7Qu9C20LjRgtC10LvRjNC90L7RgdGC0Ywg0LbQuNC30L3QuCDQsiDQs9C+0LTQsNGFIC0tLSAqKtC60L7Qu9C40YfQtdGB0YLQstC10L3QvdGL0Lkg0L3QtdC/0YDQtdGA0YvQstC90YvQuSDQv9GA0LjQt9C90LDQuioqCjguICoqKkdFU1RUSU1FKioqIC0tLSDQstGA0LXQvNGPINCx0LXRgNC10LzQtdC90L3QvtGB0YLQuCDQsiDQtNC90Y/RhSAtLS0gKirQutC+0LvQuNGH0LXRgdGC0LLQtdC90L3Ri9C5INC90LXQv9GA0LXRgNGL0LLQvdGL0Lkg0L/RgNC40LfQvdCw0LoqKgo5LiAqKipQUkVEX0lORCoqKiAtLS0g0LjQvdC00LXQutGBINGF0LjRidC90LjRh9C10YHRgtCy0LAgKDEgLS0tINC90LDQuNC80LXQvdC10LUg0LLQtdGA0L7Rj9GC0L3Ri9C5INC+0LHRitC10LrRgiDQtNC70Y8g0L7RhdC+0YLRiywgNSAtLS0g0L3QsNC40LHQvtC70LXQtSDQstC10YDQvtGP0YLQvdGL0Lkg0L7QsdGK0LXQutGCINC00LvRjyDQvtGF0L7RgtGLKSAtLS0gKirQv9C+0YDRj9C00LrQvtCy0YvQuSDQv9GA0LjQt9C90LDQuioqCjEwLiAqKipFWFBfSU5EKioqIC0tLSDQuNC90LTQtdC60YEg0LLQvtC30LTQtdC50YHRgtCy0LjRjyDQstC+INCy0YDQtdC80Y8g0YHQvdCwICgxIC0tLSDQvdCw0LjQvNC10L3QtdC1INC/0L7QtNCy0LXRgNC20LXQvdC90YvQuSDQstC+0LfQtNC10LnRgdGC0LLQuNGOICjQvdCw0L/RgNC40LzQtdGALCDQttC40LLQvtGC0L3QvtC1INGB0L/QuNGCINCyCtGF0L7RgNC+0YjQviDQt9Cw0YnQuNGJ0LXQvdC90L7QvCDQu9C+0LPQvtCy0LUpLCA1IC0tLSDQvdCw0LjQsdC+0LvQtdC1INC/0L7QtNCy0LXRgNC20LXQvdC90YvQuSDQstC+0LfQtNC10LnRgdGC0LLQuNGOKSAtLS0gKirQv9C+0YDRj9C00LrQvtCy0YvQuSDQv9GA0LjQt9C90LDQuioqCjExLiAqKipEQU5HX0lORCoqKiAtLS0g0L7QsdGJ0LjQuSDQuNC90LTQtdC60YEg0L7Qv9Cw0YHQvdC+0YHRgtC4ICjQvdCwINC+0YHQvdC+0LLQtSDQtNCy0YPRhSDQstGL0YjQtdGD0LrQsNC30LDQvdC90YvRhSDQuNC90LTQtdC60YHQvtCyINC4INC00YDRg9Cz0L7QuSDQuNC90YTQvtGA0LzQsNGG0LjQuCkgKDEgLS0tINC90LDQuNC80LXQvdGM0YjQsNGPINC+0L/QsNGB0L3QvtGB0YLRjCAo0L7RgiDQtNGA0YPQs9C40YUg0LbQuNCy0L7RgtC90YvRhSksIDUgLS0tINC90LDQuNCx0L7Qu9GM0YjQsNGPINC+0L/QsNGB0L3QvtGB0YLRjCAo0YHQviDRgdGC0L7RgNC+0L3RiyDQtNGA0YPQs9C40YUg0LbQuNCy0L7RgtC90YvRhSkpIC0tLSAqKtC/0L7RgNGP0LTQutC+0LLRi9C5INC/0YDQuNC30L3QsNC6KioKCiMjIDMuINCc0LDRgtGA0LjRh9C90YvQuSDQs9GA0LDRhNC40Log0YDQsNC30LHRgNC+0YHQsAoKYGBge3J9CmNhdGVnb3JpZXMgPC1saXN0KE5VTEwsIE5VTEwsIE5VTEwsIE5VTEwsIE5VTEwsIE5VTEwsIE5VTEwsIDE6NSwgMTo1LCAxOjUpCmRmIHw+IGRwbHlyOjpzZWxlY3QoLU5BTUUpIHw+IHNjYXR0ZXJQbG90TWF0cml4KHJlZ3Jlc3Npb25UeXBlID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgY29yclBsb3RUeXBlID0gIlRleHQiLAogICAgICAgICAgICAgICAgICAgICAgICBzbGlkZXJzUG9zaXRpb24gPSBsaXN0KAogICAgICAgICAgICAgICAgICAgICAgICAgIGRpbUNvdW50ID0gNywKICAgICAgICAgICAgICAgICAgICAgICAgICB4U3RhcnRpbmdEaW1JbmRleCA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgeVN0YXJ0aW5nRGltSW5kZXggPSAxCiAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnb3JpY2FsID0gY2F0ZWdvcmllcywKICAgICAgICAgICAgICAgICAgICAgICAgcGxvdFByb3BlcnRpZXMgPSBsaXN0KG5vQ2F0Q29sb3IgPSAiSW5kaWdvIiksCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRyb2xXaWRnZXRzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgaGVpZ2h0ID0gMTA1MCwgd2lkdGggPSAxMDAwKQpgYGAKCtCS0LjQtNC40Lwg0LTQvtGB0YLQsNGC0L7Rh9C90L4g0LvQvtCz0LjRh9C90YvQtSDQutCw0L3QtNC40LTQsNGC0Ysg0L3QsCDQu9C+0LPQsNGA0LjRhNC80LjRgNC+0LLQsNC90LjQtTogKirQstC10YEg0YLQtdC70LAqKiwgKirQstC10YEg0LzQvtC30LPQsCoqLCAqKtC/0YDQvtC00L7Qu9C20LjRgtC10LvRjNC90L7RgdGC0Ywg0LbQuNC30L3QuCoqINC4ICoq0LLRgNC10LzRjyDQsdC10YDQtdC80LXQvdC90L7RgdGC0LgqKiAtLS0g0Y3RgtC+INC80YPQu9GM0YLQuNC/0LvQtdC60LDRgtC40LLQvdGL0LUg0L/RgNC40LfQvdCw0LrQuC4g0KLQsNC60LbQtSAqKtCy0YDQtdC80Y8g0LHRi9GB0YLRgNC+0LPQviDRgdC90LAqKiDQuNC80LXQtdGCINC/0LXRgNC10LrQvtGBINCy0LvQtdCy0L4sINGC0LDQuiDRh9GC0L4g0LvRg9GH0YjQtSDQtdCz0L4g0YLQvtC20LUg0L7RgtC70L7Qs9Cw0YDQuNGE0LzQuNGA0L7QstCw0YLRjCwg0YXQvtGC0Y8g0LvQvtCz0LjRh9C10YHQutC4LCDRgtCw0Log0LrQsNC6ICoq0LLRgNC10LzRjyDRgdC90LAqKiDRgdC+0LTQtdGA0LbQuNGCINCyINGB0LXQsdC1ICoq0LLRgNC10LzRjyDQsdGL0YHRgtGA0L7Qs9C+INGB0L3QsCoqLCDRjdGC0L7RgiDQv9GA0LjQt9C90LDQuiDQvdC1INGP0LLQu9GP0LXRgtGB0Y8g0LzRg9C70YzRgtC40L/Qu9C10LrQsNGC0LjQstC90YvQvCwg0LAg0YLQsNC60LbQtSDQvdC1INC40LzQtdC10YIg0L3QtdC70LjQvdC10LnQvdGD0Y4g0LfQsNCy0LjRgdC40LzQvtGB0YLRjC4g0J7QtNC90LDQutC+LCDQsNGB0YHQuNC80LXRgtGA0LjRjyAqKtCy0YDQtdC80Lgg0LHRi9GB0YLRgNC+0LPQviDRgdC90LAqKiDQuNC80LXQtdGCINC00L7RgdGC0LDRgtC+0YfQvdC+INCx0L7Qu9GM0YjQvtC1INC30L3QsNGH0LXQvdC40LU6CgpgYGB7cn0KZGVzYzAgPC0gZGYgfD5kcGx5cjo6c2VsZWN0KC1OQU1FLCAtUFJFRF9JTkQsIC1FWFBfSU5ELCAtREFOR19JTkQpIHw+IGRlc2NyaWJlKCkgfD5kcGx5cjo6c2VsZWN0KC10cmltbWVkLCAtbWFkLCAtc2UpCnByaW50X2RmKGRlc2MwKQpgYGAKCiMjIDQuINCb0L7Qs9Cw0YDQuNGE0LzQuNGA0L7QstCw0L3QuNC1INGF0LLQvtGB0YLQsNGC0YvRhQoKYGBge3J9CmRmLmxvZyA8LSBkZgpkZi5sb2cgPC0gZGYubG9nIHw+IG11dGF0ZShCT0RZX1dFSSA9IGxvZyhCT0RZX1dFSSksIEJSQUlOX1dFID0gbG9nKEJSQUlOX1dFKSwgUEFSQURPWCA9IGxvZyhQQVJBRE9YKSwgTElGRVNQQU4gPSBsb2coTElGRVNQQU4pLCBHRVNUVElNRSA9IGxvZyhHRVNUVElNRSkpCmRmLmxvZyA8LSBkZi5sb2cgfD4gbXV0YXRlKFBBUkFET1ggPSBpZmVsc2UoaXMuaW5maW5pdGUoUEFSQURPWCksIE5BLCBQQVJBRE9YKSkKZGYubG9nIHw+IGRwbHlyOjpzZWxlY3QoLU5BTUUpIHw+IGZpbHRlcighaXMubmEoU0xPV1dBVkUpICYgIWlzLm5hKFBBUkFET1gpICYgIWlzLm5hKFNMRUVQKSAmICFpcy5uYShMSUZFU1BBTikgJiAhaXMubmEoR0VTVFRJTUUpKSB8PiBzY2F0dGVyUGxvdE1hdHJpeChyZWdyZXNzaW9uVHlwZSA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgIGNvcnJQbG90VHlwZSA9ICJUZXh0IiwKICAgICAgICAgICAgICAgICAgICAgICAgc2xpZGVyc1Bvc2l0aW9uID0gbGlzdCgKICAgICAgICAgICAgICAgICAgICAgICAgICBkaW1Db3VudCA9IDcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgeFN0YXJ0aW5nRGltSW5kZXggPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgIHlTdGFydGluZ0RpbUluZGV4ID0gMQogICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICBjYXRlZ29yaWNhbCA9IGNhdGVnb3JpZXMsCiAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RQcm9wZXJ0aWVzID0gbGlzdChub0NhdENvbG9yID0gIkluZGlnbyIpLAogICAgICAgICAgICAgICAgICAgICAgICBjb250cm9sV2lkZ2V0cyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgIGhlaWdodCA9IDEwNTAsIHdpZHRoID0gMTAwMCkKYGBgCgrQktGL0LLQtdC00LXQvCDRgtCw0LrQttC1INC90LXQvNC90L7Qs9C+INC00YDRg9Cz0L7QuSDQvNCw0YLRgNC40YfQvdGL0Lkg0LPRgNCw0YTQuNC6LCDQsiDQutC+0YLQvtGA0L7QvCDRg9C00LDQu9C10L3QuNC1IE5BINCx0YPQtNC10YIg0L/QvtC/0LDRgNC90YvQvC4KCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQpkZi5sb2cgfD4gZHBseXI6OnNlbGVjdCgtTkFNRSkgfD4gZ2dwYWlycyhjb2x1bW5zID0gMTo3KQpgYGAKCtCS0LjQtNC40LwsINGH0YLQviDRgNCw0YHQv9GA0LXQtNC10LvQtdC90LjRjyDRgdGC0LDQu9C4INCx0L7Qu9C10LUg0YHQuNC80LzQtdGC0YDQuNGH0L3Ri9C80LgsINC/0YDQuNGH0ZHQvCDQv9C+0YfRgtC4INCy0YHQtSDQv9GA0LjQt9C90LDQutC4INC40LzQtdGO0YIg0L7RgtGA0LjRhtCw0YLQtdC70YzQvdGL0Lkg0Y3QutGB0YbQtdGB0YEsINGC0L4g0LXRgdGC0Ywg0L7QvdC4INCx0L7Qu9C10LUgItC/0LvQvtGB0LrQuNC1Iiwg0YfQtdC8INC90L7RgNC80LDQu9GM0L3QvtC1OgoKYGBge3J9CmRlc2MxIDwtIGRmLmxvZyB8PmRwbHlyOjpzZWxlY3QoLU5BTUUsIC1QUkVEX0lORCwgLUVYUF9JTkQsIC1EQU5HX0lORCkgfD4gZGVzY3JpYmUoKSB8PmRwbHlyOjpzZWxlY3QoLXRyaW1tZWQsIC1tYWQsIC1zZSkKcHJpbnRfZGYoZGVzYzEpCmBgYAoK0J/QviDQs9GA0LDRhNC40LrRgyDQvdC10YDQsNCy0L3QvtC80LXRgNC90L7RgdGC0LXQuSDQvdC1INCy0LjQtNC90L4uINCV0YHQu9C4INGA0LDRgdC60YDQsNGB0LjRgtGMINC/0L4g0LrQsNGC0LXQs9C+0YDQuNCw0LvRjNC90YvQvCDQv9GA0LjQt9C90LDQutCw0LwsINGC0L4g0YLQvtC20LUg0L3QtSDQv9C+0LvRg9GH0LDQtdGC0YHRjyDQstGL0LTQtdC70LjRgtGMINGA0LDQt9C00LXQu9GM0L3Ri9C1ICrQvtCx0LvQsNC60LAqLgoKIyMgNS4g0JLRi9Cx0YDQvtGB0YsKCtCS0LjQtNC40Lwg0L/QsNGA0YMg0LLRi9Cx0YDQvtGB0L7Qsiwg0LTQu9GPINC60L7RgtC+0YDRi9GFINCy0LXRgSDQvNC+0LfQs9CwINC80LDQuywg0L3QviDQv9GA0L7QtNC+0LvQttC40YLQtdC70YzQvdC+0YHRgtGMINC20LjQt9C90Lgg0LTQvtGB0YLQsNGC0L7Rh9C90L4g0LLQtdC70LjQutCwOgoKYGBge3J9CmRmLmxvZyB8PiBmaWx0ZXIoTElGRVNQQU4gPiAyICYgQlJBSU5fV0UgPCAwKSB8PiBwcmludF9kZigpCmBgYAoK0K3RgtC+INC00LLQtSAqKtC70LXRgtGD0YfQuNC1INC80YvRiNC4KiouCgrQn9C+0YHRgtGA0L7QuNC8INGC0LXQv9C10YDRjCDQs9GA0LDRhNC40LrQuCDRgNCw0LfQsdGA0L7RgdCwINCx0LXQtyDQstGL0LHRgNC+0YHQvtCyLgoKYGBge3J9CmRmLmxvZy5vdXQgPC0gZGYubG9nIHw+ICBmaWx0ZXIoIShMSUZFU1BBTiA+IDIgJiBCUkFJTl9XRSA8IDApKQpkZi5sb2cub3V0IHw+IGRwbHlyOjpzZWxlY3QoLU5BTUUpIHw+IGZpbHRlcighaXMubmEoU0xPV1dBVkUpICYgIWlzLm5hKFBBUkFET1gpICYgIWlzLm5hKFNMRUVQKSAmICFpcy5uYShMSUZFU1BBTikgJiAhaXMubmEoR0VTVFRJTUUpKSB8PiBzY2F0dGVyUGxvdE1hdHJpeChyZWdyZXNzaW9uVHlwZSA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgIGNvcnJQbG90VHlwZSA9ICJUZXh0IiwKICAgICAgICAgICAgICAgICAgICAgICAgc2xpZGVyc1Bvc2l0aW9uID0gbGlzdCgKICAgICAgICAgICAgICAgICAgICAgICAgICBkaW1Db3VudCA9IDcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgeFN0YXJ0aW5nRGltSW5kZXggPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgIHlTdGFydGluZ0RpbUluZGV4ID0gMQogICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICBjYXRlZ29yaWNhbCA9IGNhdGVnb3JpZXMsCiAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RQcm9wZXJ0aWVzID0gbGlzdChub0NhdENvbG9yID0gIkluZGlnbyIpLAogICAgICAgICAgICAgICAgICAgICAgICBjb250cm9sV2lkZ2V0cyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgIGhlaWdodCA9IDEwNTAsIHdpZHRoID0gMTAwMCkKYGBgCgrQmCDRgdGA0LDQstC90LjQvCDQutC+0YDRgNC10LvRj9GG0LjQuC4KCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQpkZi5sb2cgfD4gZHBseXI6OnNlbGVjdCgtTkFNRSkgfD4gZ2dwYWlycyhjb2x1bW5zID0gMTo3KQpkZi5sb2cub3V0IHw+IGRwbHlyOjpzZWxlY3QoLU5BTUUpIHw+IGdncGFpcnMoY29sdW1ucyA9IDE6NykKYGBgCgrQkiDQvtGB0L3QvtCy0L3QvtC8INC+0L3QuCAqKtCy0YvRgNC+0YHQu9C4KiosINC/0L4g0LrRgNCw0LnQvdC10Lkg0LzQtdGA0LUsINGC0LDQvCwg0LPQtNC1INC80Ysg0LjRgdC60LvRjtGH0LDQu9C4INCy0YvQsdGA0L7RgdGLLgoKIyMgNi4g0JfQsNCy0LjRgdC40LzQvtGB0YLQuCDQvtGCINC40L3QtNC10LrRgdC+0LIg0L7Qv9Cw0YHQvdC+0YHRgtC4CgrQndC10L7QsdGF0L7QtNC40LzQviDQvtC/0LjRgdCw0YLRjCDRgNCw0LfQvdC40YbRgyDQvNC10LbQtNGDINC20LjQstC+0YLQvdGL0LzQuCDQv9C+INC40L3QtNC10LrRgdGDINC+0L/QsNGB0L3QvtGB0YLQuCDQvNC10YHRgtCwLCDQs9C00LUg0L7QvdC4INGB0L/Rj9GCLiDQn9C+0YHQvNC+0YLRgNC40Lwg0L3QsCDQutC+0LvQuNGH0LXRgdGC0LLQviDQttC40LLQvtGC0L3Ri9GFINCyINC60LDQttC00L7QuSDQs9GA0YPQv9C/0LU6CgpgYGB7cn0KZGYubG9nLm91dCB8PmRwbHlyOjpzZWxlY3QoRVhQX0lORCkgfD4gc3VtbWFyeSgpCmBgYAoK0JLRi9Cx0L7RgNC60Lgg0LTQu9GPINC40L3QtNC10LrRgdC+0LIgJDIkINC4ICQ1JCDQvNC+0LbQtdC8INGB0YfQuNGC0LDRgtGMINGB0LHQsNC70LDQvdGB0LjRgNC+0LLQsNC90L3Ri9C80LguCgrQktC40LfRg9Cw0LvRjNC90L4g0L7RhtC10L3QuNC8INGA0LDRgdC/0YDQtdC00LXQu9C10L3QuNGPINGBINC/0L7QvNC+0YnRjNGOINGP0YnQuNC60L7QsiDRgSDRg9GB0LDQvNC4OgoKYGBge3J9CmRmLmxvZy5vdXQgfD4gZ2dwbG90KGFlcyh4ID0gRVhQX0lORCwgeSA9IEJPRFlfV0VJKSkgKwogIGdlb21fYm94cGxvdCgpCmBgYAoKYGBge3J9CmRmLmxvZy5vdXQgfD4gZ2dwbG90KGFlcyh4ID0gRVhQX0lORCwgeSA9IEJSQUlOX1dFKSkgKwogIGdlb21fYm94cGxvdCgpCmBgYAoKYGBge3Igd2FybmluZz1GQUxTRX0KZGYubG9nLm91dCB8PiBnZ3Bsb3QoYWVzKHggPSBFWFBfSU5ELCB5ID0gUEFSQURPWCkpICsKICBnZW9tX2JveHBsb3QoKQpgYGAKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CmRmLmxvZy5vdXQgfD4gZ2dwbG90KGFlcyh4ID0gRVhQX0lORCwgeSA9IFNMT1dXQVZFKSkgKwogIGdlb21fYm94cGxvdCgpCmBgYAoKYGBge3J9CmRmLmxvZy5vdXQgfD4gZ2dwbG90KGFlcyh4ID0gRVhQX0lORCwgeSA9IFNMRUVQKSkgKwogIGdlb21fYm94cGxvdCgpCmBgYAoKYGBge3Igd2FybmluZz1GQUxTRX0KZGYubG9nLm91dCB8PiBnZ3Bsb3QoYWVzKHggPSBFWFBfSU5ELCB5ID0gTElGRVNQQU4pKSArCiAgZ2VvbV9ib3hwbG90KCkKYGBgCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpkZi5sb2cub3V0IHw+IGdncGxvdChhZXMoeCA9IEVYUF9JTkQsIHkgPSBHRVNUVElNRSkpICsKICBnZW9tX2JveHBsb3QoKQpgYGAKCtCSINC+0YHQvdC+0LLQvdC+0Lwg0LLQuNC00L3QsCDQvNC+0L3QvtGC0L7QvdC90LDRjyDQt9Cw0LLQuNGB0LjQvNC+0YHRgtGMLCDQvtC00L3QsNC60L4sINC/0YDQtdC00YHRgtC+0LjRgiDQv9GA0L7QstC10YDQuNGC0Ywg0Y3RgtC+INGBINC/0L7QvNC+0YnRjNGOINC60YDQuNGC0LXRgNC40LXQsi4KCiMjIDcuINCd0L7RgNC80LDQu9GM0L3QvtGB0YLRjCwg0LLQuNC0INGA0LDRgdC/0YDQtdC00LXQu9C10L3QuNGPCgrQmNC3LdC30LAg0LzQsNC70L7RgdGC0Lgg0LLRi9Cx0L7RgNC+0Log0LIg0YLRgNC10YLRjNC10Lkg0Lgg0YfQtdGC0LLRkdGA0YLQvtC5INCz0YDRg9C/0L/QsNGFINC40YHQutC70Y7Rh9C40Lwg0LjRhSDQuNC3INC40YHRgdC70LXQtNC+0LLQsNC90LjRjy4KCmBgYHtyfQpnZXRfZGZfb25faW5kIDwtIGZ1bmN0aW9uKGkpIHsKICByZXR1cm4oZGYubG9nLm91dCB8PiBmaWx0ZXIoRVhQX0lORCA9PSBpKSkKfQpgYGAKCtCf0L7RgdC80L7RgtGA0LjQvCDRgdC90LDRh9Cw0LvQsCDQvdCwINCz0LjRgdGC0L7Qs9GA0LDQvNC80Ysg0YDQsNGB0L/RgNC10LTQtdC70LXQvdC40Lkg0L/QviDQs9GA0YPQv9C/0LDQvCDQtNC70Y8g0LrQsNC20LTQvtCz0L4g0L/RgNC40LfQvdCw0LrQsC4KCmBgYHtyfQpmb3IgKGkgaW4gYygxLCAyLCA1KSkgewogIHByaW50KHBhc3RlKCLQk9GA0YPQv9C/0LAgIiwgaSkpCiAgcGFyKG1mcm93ID0gYygyLCA0KSkKICBoaXN0KGdldF9kZl9vbl9pbmQoaSkkQk9EWV9XRUksIG1haW4gPSAiIikKICBoaXN0KGdldF9kZl9vbl9pbmQoaSkkQlJBSU5fV0UsIG1haW4gPSAiIikKICBoaXN0KGdldF9kZl9vbl9pbmQoaSkkU0xPV1dBVkUsIG1haW4gPSAiIikKICBoaXN0KGdldF9kZl9vbl9pbmQoaSkkUEFSQURPWCwgbWFpbiA9ICIiKQogIGhpc3QoZ2V0X2RmX29uX2luZChpKSRTTEVFUCwgbWFpbiA9ICIiKQogIGhpc3QoZ2V0X2RmX29uX2luZChpKSRMSUZFU1BBTiwgbWFpbiA9ICIiKQogIGhpc3QoZ2V0X2RmX29uX2luZChpKSRHRVNUVElNRSwgbWFpbiA9ICIiKQp9CmBgYAoK0J/QvtGB0LzQvtGC0YDQuNC8INC90LAg0L3QvtGA0LzQsNC70YzQvdC+0YHRgtGMINGBINC/0L7QvNC+0YnRjNGOIE5vcm1hbCBQcm9iYWJpbGl0eSBQbG90OgoKYGBge3J9CmZvciAoaSBpbiBjKDEsIDIsIDUpKSB7CiAgcHJpbnQocGFzdGUoItCT0YDRg9C/0L/QsCAiLCBpKSkKICBwYXIobWZyb3cgPSBjKDIsIDQpKQogIHBsb3RfcXFfZ3JhcGgoZ2V0X2RmX29uX2luZChpKSRCT0RZX1dFSSwgIkJPRFlfV0VJIikKICBwbG90X3FxX2dyYXBoKGdldF9kZl9vbl9pbmQoaSkkQlJBSU5fV0UsICJCUkFJTl9XRSIpCiAgcGxvdF9xcV9ncmFwaChnZXRfZGZfb25faW5kKGkpJFNMT1dXQVZFLCAiU0xPV1dBVkUiKQogIHBsb3RfcXFfZ3JhcGgoZ2V0X2RmX29uX2luZChpKSRQQVJBRE9YLCAiUEFSQURPWCIpCiAgcGxvdF9xcV9ncmFwaChnZXRfZGZfb25faW5kKGkpJFNMRUVQLCAiU0xFRVAiKQogIHBsb3RfcXFfZ3JhcGgoZ2V0X2RmX29uX2luZChpKSRMSUZFU1BBTiwgIkxJRkVTUEFOIikKICBwbG90X3FxX2dyYXBoKGdldF9kZl9vbl9pbmQoaSkkR0VTVFRJTUUsICJHRVNUVElNRSIpCn0KYGBgCgrQmCDRgSDQv9C+0LzQvtGJ0YzRjiBQUC1wbG90OgoKYGBge3J9CmZvciAoaSBpbiBjKDEsIDIsIDUpKSB7CiAgcHJpbnQocGFzdGUoItCT0YDRg9C/0L/QsCAiLCBpKSkKICBwYXIobWZyb3cgPSBjKDIsIDQpKQogIHBsb3RfcHBfcGxvdCgoZ2V0X2RmX29uX2luZChpKSB8PiBmaWx0ZXIoIWlzLm5hKEJPRFlfV0VJKSkpJEJPRFlfV0VJLCAiQk9EWV9XRUkiKQogIHBsb3RfcHBfcGxvdCgoZ2V0X2RmX29uX2luZChpKSB8PiBmaWx0ZXIoIWlzLm5hKEJSQUlOX1dFKSkpJEJSQUlOX1dFLCAiQlJBSU5fV0UiKQogIHBsb3RfcHBfcGxvdCgoZ2V0X2RmX29uX2luZChpKSB8PiBmaWx0ZXIoIWlzLm5hKFNMT1dXQVZFKSkpJFNMT1dXQVZFLCAiU0xPV1dBVkUiKQogIHBsb3RfcHBfcGxvdCgoZ2V0X2RmX29uX2luZChpKSB8PiBmaWx0ZXIoIWlzLm5hKFBBUkFET1gpKSkkUEFSQURPWCwgIlBBUkFET1giKQogIHBsb3RfcHBfcGxvdCgoZ2V0X2RmX29uX2luZChpKSB8PiBmaWx0ZXIoIWlzLm5hKFNMRUVQKSkpJFNMRUVQLCAiU0xFRVAiKQogIHBsb3RfcHBfcGxvdCgoZ2V0X2RmX29uX2luZChpKSB8PiBmaWx0ZXIoIWlzLm5hKExJRkVTUEFOKSkpJExJRkVTUEFOLCAiTElGRVNQQU4iKQogIHBsb3RfcHBfcGxvdCgoZ2V0X2RmX29uX2luZChpKSB8PiBmaWx0ZXIoIWlzLm5hKEdFU1RUSU1FKSkpJEdFU1RUSU1FLCAiR0VTVFRJTUUiKQp9CmBgYAoK0JAg0YLQtdC/0LXRgNGMINC+0YbQtdC90LjQvCDQv9C+INGC0LXRgdGC0YMg0KjQsNC/0LjRgNC+LdCj0LjQu9C60LA6CgpgYGB7cn0KZGYuc2hhcGlyby50ZXN0LnAudmFsdWUgPC0gZGF0YS5mcmFtZShpbmQgPSBjKDEsIDIsIDUpLCBCT0RZX1dFSV9QID0gcmVwKDAsIDMpLCBCUkFJTl9XRV9QID0gcmVwKDAsIDMpLCBTTE9XV0FWRV9QID0gcmVwKDAsIDMpLCBQQVJBRE9YX1AgPSByZXAoMCwgMyksIFNMRUVQX1AgPSByZXAoMCwgMyksIExJRkVTUEFOX1AgPSByZXAoMCwgMyksIEdFU1RUSU1FX1AgPSByZXAoMCwgMykpCgpmb3IgKGkgaW4gYygxLCAyLCA1KSkgewogIGRmLnNoYXBpcm8udGVzdC5wLnZhbHVlW2RmLnNoYXBpcm8udGVzdC5wLnZhbHVlJGluZCA9PSBpLCBdIDwtIGMoaSwgc2hhcGlyby50ZXN0KGdldF9kZl9vbl9pbmQoaSkkQk9EWV9XRUkpJHAudmFsdWUsIAogIHNoYXBpcm8udGVzdChnZXRfZGZfb25faW5kKGkpJEJSQUlOX1dFKSRwLnZhbHVlLAogIHNoYXBpcm8udGVzdChnZXRfZGZfb25faW5kKGkpJFNMT1dXQVZFKSRwLnZhbHVlLAogIHNoYXBpcm8udGVzdChnZXRfZGZfb25faW5kKGkpJFBBUkFET1gpJHAudmFsdWUsCiAgc2hhcGlyby50ZXN0KGdldF9kZl9vbl9pbmQoaSkkU0xFRVApJHAudmFsdWUsCiAgc2hhcGlyby50ZXN0KGdldF9kZl9vbl9pbmQoaSkkTElGRVNQQU4pJHAudmFsdWUsCiAgc2hhcGlyby50ZXN0KGdldF9kZl9vbl9pbmQoaSkkR0VTVFRJTUUpJHAudmFsdWUpCn0KCmRmLnNoYXBpcm8udGVzdC5wLnZhbHVlIHw+IHByaW50X2RmKCkKYGBgCgrQmCDQvdCw0LrQvtC90LXRhiDQv9C+INCb0LjQu9C70LjQtdGE0L7RgNGB0YM6CgpgYGB7cn0KZGYubGlsbGllLnRlc3QucC52YWx1ZSA8LSBkYXRhLmZyYW1lKGluZCA9IGMoMSwgMiwgNSksIEJPRFlfV0VJX1AgPSByZXAoMCwgMyksIEJSQUlOX1dFX1AgPSByZXAoMCwgMyksIFNMT1dXQVZFX1AgPSByZXAoMCwgMyksIFBBUkFET1hfUCA9IHJlcCgwLCAzKSwgU0xFRVBfUCA9IHJlcCgwLCAzKSwgTElGRVNQQU5fUCA9IHJlcCgwLCAzKSwgR0VTVFRJTUVfUCA9IHJlcCgwLCAzKSkKCmZvciAoaSBpbiBjKDEsIDIsIDUpKSB7CiAgZGYubGlsbGllLnRlc3QucC52YWx1ZVtkZi5saWxsaWUudGVzdC5wLnZhbHVlJGluZCA9PSBpLCBdIDwtIGMoaSwgbGlsbGllLnRlc3QoZ2V0X2RmX29uX2luZChpKSRCT0RZX1dFSSkkcC52YWx1ZSwgCiAgbGlsbGllLnRlc3QoZ2V0X2RmX29uX2luZChpKSRCUkFJTl9XRSkkcC52YWx1ZSwKICBsaWxsaWUudGVzdChnZXRfZGZfb25faW5kKGkpJFNMT1dXQVZFKSRwLnZhbHVlLAogIGxpbGxpZS50ZXN0KGdldF9kZl9vbl9pbmQoaSkkUEFSQURPWCkkcC52YWx1ZSwKICBsaWxsaWUudGVzdChnZXRfZGZfb25faW5kKGkpJFNMRUVQKSRwLnZhbHVlLAogIGxpbGxpZS50ZXN0KGdldF9kZl9vbl9pbmQoaSkkTElGRVNQQU4pJHAudmFsdWUsCiAgbGlsbGllLnRlc3QoZ2V0X2RmX29uX2luZChpKSRHRVNUVElNRSkkcC52YWx1ZSkKfQoKZGYubGlsbGllLnRlc3QucC52YWx1ZSB8PiBwcmludF9kZigpCmBgYAoK0K3RgtC+0YIg0LrRgNC40YLQtdGA0LjQuSDRj9Cy0LvRj9C10YLRgdGPINC80LXQvdC10LUg0LzQvtGJ0L3Ri9C8LCDQvtC00L3QsNC60L4g0LTQsNGR0YIg0YHRhdC+0LbQuNC1INGA0LXQt9GD0LvRjNGC0LDRgtGLLgoK0J/QviDQuNGC0L7Qs9GDINC80L7QttC90L4g0YHRh9C40YLQsNGC0YwsINGH0YLQviDRgNCw0YHQv9GA0LXQtNC10LvQtdC90LjRjyDQstGB0LXRhSDQv9GA0LjQt9C90LDQutC+0LIg0LLQviDQstGB0LXRhSDQs9GA0YPQv9C/0LDRhSDQv9C+0YXQvtC20Lgg0L3QsCDQvdC+0YDQvNCw0LvRjNC90YvQtSwg0LrRgNC+0LzQtSAqKtCy0LXRgdCwINGC0LXQu9CwKiog0LggKirQstC10YHQsCDQvNC+0LfQs9CwKiog0LLQviDQstGC0L7RgNC+0Lkg0LPRgNGD0L/Qv9C1INC4ICoq0LLRgNC10LzQtdC90Lgg0LzQtdC00LvQtdC90L3QvtCz0L4g0YHQvdCwKiosICoq0LLRgNC10LzQtdC90Lgg0YHQvdCwKiog0LggKirQstGA0LXQvNC10L3QuCDQsdC10YDQtdC80LXQvdC90L7RgdGC0LgqKiDQsiDQv9GP0YLQvtC5INCz0YDRg9C/0L/QtS4gCgojIyA4LiB0LdGC0LXRgdGC0YsKCtCh0L3QsNGH0LDQu9CwINC/0L7RgdC80L7RgtGA0LjQvCDQvdCwINC00LjRgdC/0LXRgNGB0LjRjiwg0YfRgtC+0LHRiyDQv9GA0LjQvNC10L3QuNGC0YwgdC10ZXN0INC00LvRjyDRgNCw0YHQv9GA0LXQtNC10LvQtdC90LjQuSDRgSDRgNCw0LLQvdGL0LzQuCDQtNC40YHQv9C10YDRgdC40Y/QvNC4LiDQotC10YHRgiDQpNC40YjQtdGA0LAg0L/RgNC+0LLQtdGA0Y/QtdGCINGA0LDQstC10L3RgdGC0LLQviDQtNC40YHQv9C10YDRgdC40Lkg0LTQu9GPINC90L7RgNC80LDQu9GM0L3QviDRgNCw0YHQv9GA0LXQtNC10LvRkdC90L3Ri9GFINC00LDQvdC90YvRhSwg0LrQvtGC0L7RgNGL0LUg0LzRiyDQvtC/0YDQtdC00LXQu9C10LvQuNC70Lgg0LIg0L/RgNC+0YjQu9C+0Lwg0L/Rg9C90LrRgtC1OgoKYGBge3J9CmRmLmZpc2hlci52YXIgPC0gZGF0YS5mcmFtZShJTkQxID0gcmVwKDAsIDMpLCBJTkQyID0gcmVwKDAsIDMpLCBCT0RZX1dFSV9QID0gcmVwKDAsIDMpLCBCUkFJTl9XRV9QID0gcmVwKDAsIDMpLCBTTE9XV0FWRV9QID0gcmVwKDAsIDMpLCBQQVJBRE9YX1AgPSByZXAoMCwgMyksIFNMRUVQX1AgPSByZXAoMCwgMyksIExJRkVTUEFOX1AgPSByZXAoMCwgMyksIEdFU1RUSU1FX1AgPSByZXAoMCwgMykpCgprIDwtIDEKCmZvciAoaSBpbiBjKDEsIDIsIDUpKXsKICBmb3IgKGogaW4gYygxLCAyLCA1KSl7CiAgICBpZiAoaSA8IGopewogICAgICBkZi5maXNoZXIudmFyW2ssIF0gPC0gYyhpLCBqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyLnRlc3QoZ2V0X2RmX29uX2luZChpKSRCT0RZX1dFSSwgZ2V0X2RmX29uX2luZChqKSRCT0RZX1dFSSkkcC52YWx1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhci50ZXN0KGdldF9kZl9vbl9pbmQoaSkkQlJBSU5fV0UsIGdldF9kZl9vbl9pbmQoaikkQlJBSU5fV0UpJHAudmFsdWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIudGVzdChnZXRfZGZfb25faW5kKGkpJFNMT1dXQVZFLCBnZXRfZGZfb25faW5kKGopJFNMT1dXQVZFKSRwLnZhbHVlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyLnRlc3QoZ2V0X2RmX29uX2luZChpKSRQQVJBRE9YLCBnZXRfZGZfb25faW5kKGopJFBBUkFET1gpJHAudmFsdWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIudGVzdChnZXRfZGZfb25faW5kKGkpJFNMRUVQLCBnZXRfZGZfb25faW5kKGopJFNMRUVQKSRwLnZhbHVlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyLnRlc3QoZ2V0X2RmX29uX2luZChpKSRMSUZFU1BBTiwgZ2V0X2RmX29uX2luZChqKSRMSUZFU1BBTikkcC52YWx1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhci50ZXN0KGdldF9kZl9vbl9pbmQoaSkkR0VTVFRJTUUsIGdldF9kZl9vbl9pbmQoaikkR0VTVFRJTUUpJHAudmFsdWUpCiAgICAgIGsgPC0gayArIDEKICAgIH0KICB9Cn0KCmRmLmZpc2hlci52YXIgfD4gcHJpbnRfZGYoKQpgYGAKCtCT0LjQv9C+0YLQtdC30LAg0L4g0YDQsNCy0LXQvdGB0YLQstC1INC90LUg0L7RgtCy0LXRgNCz0LDQtdGC0YHRjyDQtNC70Y8gKirQstC10YHQsCDRgtC10LvQsCoqINC4ICoq0LLQtdGB0LAg0LzQvtC30LPQsCoqINC80LXQttC00YMg0L/QtdGA0LLQvtC5INC4INC/0Y/RgtC+0Lkg0LPRgNGD0L/Qv9GLLCDQtNC70Y8gKirQstGA0LXQvNC10L3QuCDQvNC10LTQu9C10L3QvdC+0LPQviDRgdC90LAqKiwgKirQstGA0LXQvNC10L3QuCDRgdC90LAqKiDQuCAqKtCy0YDQtdC80LXQvdC4INCx0LXRgNC10LzQtdC90L3QvtGB0YLQuCoqINC80LXQttC00YMg0L/QtdGA0LLQvtC5INC4INCy0YLQvtGA0L7QuSDQs9GA0YPQv9C/0YssICoq0LLRgNC10LzQtdC90Lgg0LHRi9GB0YLRgNC+0LPQviDRgdC90LAqKiDQvNC10LbQtNGDINCy0YHQtdC80Lgg0LPRgNGD0L/Qv9Cw0LzQuCDQuCAqKtC/0YDQvtC00L7Qu9C20LjRgtC10LvRjNC90L7RgdGC0Lgg0LbQuNC30L3QuCoqINC80LXQttC00YMg0L/QtdGA0LLQvtC5INC4INCy0YLQvtGA0L7QuSDQs9GA0YPQv9C/0L7QuS4KCtCf0YDQvtCy0LXQtNGR0LwgdC3RgtC10YHRgtGLINC00LvRjyDQv9C10YDQstC+0LksINCy0YLQvtGA0L7QuSDQuCDQv9GP0YLQvtC5INCz0YDRg9C/0L/RiyDQv9C+INCy0YHQtdC8INC/0YDQuNC30L3QsNC60LDQvDoKCmBgYHtyfQpkZi50LnRlc3QucC52YWx1ZSA8LSBkYXRhLmZyYW1lKElORDEgPSByZXAoMCwgMyksIElORDIgPSByZXAoMCwgMyksIEJPRFlfV0VJX1AgPSByZXAoMCwgMyksIEJSQUlOX1dFX1AgPSByZXAoMCwgMyksIFNMT1dXQVZFX1AgPSByZXAoMCwgMyksIFBBUkFET1hfUCA9IHJlcCgwLCAzKSwgU0xFRVBfUCA9IHJlcCgwLCAzKSwgTElGRVNQQU5fUCA9IHJlcCgwLCAzKSwgR0VTVFRJTUVfUCA9IHJlcCgwLCAzKSkKCmsgPC0gMQpmbGFnIDwtIEZBTFNFCmZsYWdCQiA8LSBGQUxTRQpmbGFnU1NHIDwtRkFMU0UKZmxhZ0wgPC0gRkFMU0UKCmZvciAoaSBpbiBjKDEsIDIsIDUpKXsKICBmb3IgKGogaW4gYygxLCAyLCA1KSl7CiAgICBpZiAoaSA8IGopewogICAgICAgIGZsYWcgPC0gaSA9PSAyICYgaiA9PSA1CiAgICAgICAgZmxhZ0JCIDwtIGkgPT0gMSAmIGogPT0gNQogICAgICAgIGZsYWdTU0cgPC0gaSA9PSAxICYgaiA9PSAyCiAgICAgICAgZmxhZ0wgPC0gaSA9PSAxICYgaiA9PSAyCiAgICAgIAogICAgICBkZi50LnRlc3QucC52YWx1ZVtrLCBdIDwtIGMoaSwgaiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHQudGVzdChnZXRfZGZfb25faW5kKGkpJEJPRFlfV0VJLCBnZXRfZGZfb25faW5kKGopJEJPRFlfV0VJLCB2YXIuZXF1YWwgPSBmbGFnIHwgZmxhZ0JCKSRwLnZhbHVlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdC50ZXN0KGdldF9kZl9vbl9pbmQoaSkkQlJBSU5fV0UsIGdldF9kZl9vbl9pbmQoaikkQlJBSU5fV0UsIHZhci5lcXVhbCA9IGZsYWcgfCBmbGFnQkIpJHAudmFsdWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0LnRlc3QoZ2V0X2RmX29uX2luZChpKSRTTE9XV0FWRSwgZ2V0X2RmX29uX2luZChqKSRTTE9XV0FWRSwgdmFyLmVxdWFsID0gZmxhZyB8IGZsYWdTU0cpJHAudmFsdWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0LnRlc3QoZ2V0X2RmX29uX2luZChpKSRQQVJBRE9YLCBnZXRfZGZfb25faW5kKGopJFBBUkFET1gsIHZhci5lcXVhbCA9IFRSVUUpJHAudmFsdWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0LnRlc3QoZ2V0X2RmX29uX2luZChpKSRTTEVFUCwgZ2V0X2RmX29uX2luZChqKSRTTEVFUCwgdmFyLmVxdWFsID0gZmxhZyB8IGZsYWdTU0cpJHAudmFsdWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0LnRlc3QoZ2V0X2RmX29uX2luZChpKSRMSUZFU1BBTiwgZ2V0X2RmX29uX2luZChqKSRMSUZFU1BBTiwgdmFyLmVxdWFsID0gZmxhZyB8IGZsYWdMKSRwLnZhbHVlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdC50ZXN0KGdldF9kZl9vbl9pbmQoaSkkR0VTVFRJTUUsIGdldF9kZl9vbl9pbmQoaikkR0VTVFRJTUUsIHZhci5lcXVhbCA9IGZsYWcgfCBmbGFnU1NHKSRwLnZhbHVlKQogICAgICBrIDwtIGsgKyAxCiAgICB9CiAgfQp9CgpkZi50LnRlc3QucC52YWx1ZSB8PiBwcmludF9kZigpCmBgYAoK0JPQuNC/0L7RgtC10LfQsCDQviDRgNCw0LLQtdC90YHRgtCy0LUg0YDQsNGB0L/RgNC10LTQtdC70LXQvdC40Lkg0L/QviDQstGB0LXQvCDQv9GA0LjQt9C90LDQutCw0Lwg0LzQtdC20LTRgyDQv9C10YDQstC+0Lkg0Lgg0LLRgtC+0YDQvtC5INC90LUg0L7RgtCy0LXRgNCz0LDQtdGC0YHRjywg0LAg0LzQtdC20LTRgyDQv9C10YDQstC+0Lkg0Lgg0L/Rj9GC0L7QuSDQuCDQstGC0L7RgNC+0Lkg0Lgg0L/Rj9GC0L7QuSDQvtGC0LLQtdGA0LPQsNC10YLRgdGPLiDQntC00L3QsNC60L4g0LTQu9GPINC/0YDQuNC30L3QsNC60L7Qsiwg0LrQvtGC0L7RgNGL0LUg0LzRiyDQv9C+0YHRh9C40YLQsNC70Lgg0L3QtdC90L7RgNC80LDQu9GM0L3Ri9C80LggKCoq0LLQtdGBINGC0LXQu9CwKiog0LggKirQstC10YEg0LzQvtC30LPQsCoqINCy0L4g0LLRgtC+0YDQvtC5INCz0YDRg9C/0L/QtSDQuCAqKtCy0YDQtdC80Y8g0LzQtdC00LvQtdC90L3QvtCz0L4g0YHQvdCwKiosICoq0LLRgNC10LzRjyDRgdC90LAqKiDQuCAqKtCy0YDQtdC80Y8g0LHQtdGA0LXQvNC10L3QvdC+0YHRgtC4Kiog0LIg0L/Rj9GC0L7QuSDQs9GA0YPQv9C/0LUpIHAtdmFsdWUg0L3QtdC70YzQt9GPINGB0YfQuNGC0LDRgtGMINCy0LXRgNC90YvQvCwg0YLQsNC6INC60LDQuiDRgNCw0LfQvNC10YDRiyDQstGL0LHQvtGA0L7QuiDQvNCw0LvRiyDQuCDRjdGC0Lgg0L/RgNC40LfQvdCw0LrQuCDQvdC1INC40LzQtdGO0YIg0L3QvtGA0LzQsNC70YzQvdC+0LPQviDRgNCw0YHQv9GA0LXQtNC10LvQtdC90LjRjy4KCiMjIDkuINCi0LXRgdGCINCc0LDQvdC90LAt0KPQuNGC0L3QuAoK0JTQu9GPINC/0YDQvtCy0LXRgNC60Lgg0YDQsNCy0LXQvdGB0YLQstCwINGA0LDRgdC/0YDQtdC00LXQu9C10L3QuNC5INC00LvRjyDQvdC10L3QvtGA0LzQsNC70YzQvdC+INGA0LDRgdC/0YDQtdC00LXQu9GR0L3QvdGL0YUg0L/RgNC40LfQvdCw0LrQvtCyINC80L7QttC10Lwg0L/RgNC40LzQtdC90LjRgtGMINC60YDQuNGC0LXRgNC40Lkg0JzQsNC90L3QsC3Qo9C40YLQvdC4LCDQvtC00L3QsNC60L4g0L/RgNC10LbQtNC1INC/0L7RgdC80L7RgtGA0LjQvCDQvdCwINGB0LjQvNC80LXRgtGA0LjRh9C90L7RgdGCINC/0L4g0LPRgNGD0L/Qv9Cw0Lw6CgpgYGB7cn0KZGYuc2tldyA8LSBkYXRhLmZyYW1lKElORCA9IGMoMSwgMiwgNSksIEJPRFlfV0VJX1AgPSByZXAoMCwgMyksIEJSQUlOX1dFX1AgPSByZXAoMCwgMyksIFNMT1dXQVZFX1AgPSByZXAoMCwgMyksIFBBUkFET1hfUCA9IHJlcCgwLCAzKSwgU0xFRVBfUCA9IHJlcCgwLCAzKSwgTElGRVNQQU5fUCA9IHJlcCgwLCAzKSwgR0VTVFRJTUVfUCA9IHJlcCgwLCAzKSkKCmZvciAoaSBpbiBjKDEsIDIsIDUpKXsKICBkZi5za2V3W2RmLnNrZXckSU5EID09IGksIF0gPC0gYyhpLCBza2V3bmVzcyhnZXRfZGZfb25faW5kKGkpJEJPRFlfV0VJLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNrZXduZXNzKGdldF9kZl9vbl9pbmQoaSkkQlJBSU5fV0UsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2tld25lc3MoZ2V0X2RmX29uX2luZChpKSRTTE9XV0FWRSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBza2V3bmVzcyhnZXRfZGZfb25faW5kKGkpJFBBUkFET1gsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2tld25lc3MoZ2V0X2RmX29uX2luZChpKSRTTEVFUCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBza2V3bmVzcyhnZXRfZGZfb25faW5kKGkpJExJRkVTUEFOLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNrZXduZXNzKGdldF9kZl9vbl9pbmQoaSkkR0VTVFRJTUUsIG5hLnJtID0gVFJVRSkpCn0KCmRmLnNrZXcgfD4gcHJpbnRfZGYoKQpgYGAKCtCS0LjQtNC40LwsINGH0YLQviDQsNGB0LjQvNC80LXRgtGA0LjRjyAqKtCy0LXRgdCwINGC0LXQu9CwKiog0LggKirQstC10YHQsCDQvNC+0LfQs9CwKiog0YMg0LLRgtC+0YDQvtC5INCz0YDRg9C/0L/RiyAo0L3QtdC90L7RgNC80LDQu9GM0L3QviDRgNCw0YHQv9GA0LXQtNC10LvRkdC90L3Ri9C1INC/0YDQuNC30L3QsNC60LgpINGB0LjQu9GM0L3QviAo0L/RgNC40LzQtdGA0L3QviDQvdCwINC/0L7RgNGP0LTQvtC6KSDQvtGC0LvQuNGH0LDQtdGC0YHRjyDQvtGCINC/0LXRgNCy0L7QuSDQuCDQv9GP0YLQvtC5INCz0YDRg9C/0L/Riy4g0JDQvdCw0LvQvtCz0LjRh9C90L46INCw0YHQuNC80LzQtdGC0YDQuNGPICoq0LLRgNC10LzQtdC90Lgg0YHQvdCwKiog0LggKirQstGA0LXQvNC10L3QuCDQsdC10YDQtdC80LXQvdC90L7RgdGC0LgqKiDQv9GP0YLQvtC5INCz0YDRg9C/0L/RiyDRgdC40LvRjNC90L4g0L7RgtC70LjRh9Cw0LXRgtGB0Y8g0L7RgiDQv9C10YDQstC+0Lkg0Lgg0L/Rj9GC0L7QuS4g0JTQu9GPINCw0YHQuNC80LzQtdGC0YDQuNC4ICoq0LLRgNC10LzQtdC90Lgg0LHRi9GB0YLRgNC+0LPQviDRgdC90LAqKiDRgyDQv9GP0YLQvtC5INCz0YDRg9C/0L/RiyDQuCDQv9C10YDQstC+0Lkg0YLQsNC60LbQtSDQvdC10YIg0L7RgdC90L7QstCw0L3QuNC5INCz0L7QstC+0YDQuNGC0Ywg0L4g0L/RgNC40LHQu9C40LfQuNGC0LXQu9GM0L3QvtC8INGA0LDQstC10L3RgdGC0LLQtSwg0L7QtNC90LDQutC+INC80LXQttC00YMg0L/Rj9GC0L7QuSDQuCDQstGC0L7RgNC+0Lkg0LPRgNGD0L/Qv9C+0Lkg0YDQsNC30L3QuNGG0LAg0LzQsNC70LAsINC80LXQvdGM0YjQtSAkMC41JCwg0YfRgtC+INC80L7QttC10YIg0LPQvtCy0L7RgNC40YLRjCDQvtCxINGD0LTQvtCy0LvQtdGC0LLQvtGA0LXQvdC40Lgg0YPRgdC70L7QstC40Lkg0LTQu9GPINC60YDQuNGC0LXRgNC40Y8sINC+0LTQvdCw0LrQviDQvdCw0LTQviDQtdGJ0ZEg0L/QvtGB0LzQvtGC0YDQtdGC0Ywg0L3QsCDQuNGB0L/RgNCw0LLQu9C10L3QvdGD0Y4g0LLRi9Cx0L7RgNC+0YfQvdGD0Y4g0LTQuNGB0L/QtdGA0YHQuNGOOgoKYGBge3J9CmRmLnZhciA8LSBkYXRhLmZyYW1lKElORCA9IGMoMSwgMiwgNSksIEJPRFlfV0VJX1AgPSByZXAoMCwgMyksIEJSQUlOX1dFX1AgPSByZXAoMCwgMyksIFNMT1dXQVZFX1AgPSByZXAoMCwgMyksIFBBUkFET1hfUCA9IHJlcCgwLCAzKSwgU0xFRVBfUCA9IHJlcCgwLCAzKSwgTElGRVNQQU5fUCA9IHJlcCgwLCAzKSwgR0VTVFRJTUVfUCA9IHJlcCgwLCAzKSkKCmZvciAoaSBpbiBjKDEsIDIsIDUpKXsKICBkZi52YXJbZGYudmFyJElORCA9PSBpLCBdIDwtIGMoaSwgdmFyKGdldF9kZl9vbl9pbmQoaSkkQk9EWV9XRUksIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcihnZXRfZGZfb25faW5kKGkpJEJSQUlOX1dFLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIoZ2V0X2RmX29uX2luZChpKSRTTE9XV0FWRSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyKGdldF9kZl9vbl9pbmQoaSkkUEFSQURPWCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyKGdldF9kZl9vbl9pbmQoaSkkU0xFRVAsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcihnZXRfZGZfb25faW5kKGkpJExJRkVTUEFOLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIoZ2V0X2RmX29uX2luZChpKSRHRVNUVElNRSwgbmEucm0gPSBUUlVFKSkKfQoKZGYudmFyIHw+IHByaW50X2RmKCkKYGBgCgrQktC40LTQuNC8LCDRh9GC0L4g0LTQuNGB0L/QtdGA0YHQuNC4ICoq0LLRgNC10LzQtdC90Lgg0LzQtdC00LvQtdC90L3QvtCz0L4g0YHQvdCwKiog0LTQu9GPINC/0Y/RgtC+0Lkg0Lgg0LLRgtC+0YDQvtC5INCz0YDRg9C/0L/RiyDQtNCw0LbQtSDQv9GA0LjQsdC70LjQt9C40YLQtdC70YzQvdC+INC90LUg0YDQsNCy0L3Riywg0L/QvtGN0YLQvtC80YMg0L3QuCDQvtC00LjQvSDQuNC3INC/0YDQuNC30L3QsNC60L7Qsiwg0LrQvtGC0L7RgNGL0LUg0LzRiyDRhdC+0YLQtdC70Lgg0L/RgNC+0LLQtdGA0LjRgtGMINC90LUg0L/QvtC00YXQvtC00Y/RgiDQtNC70Y8g0L/RgNC40LzQtdC90LXQvdC40Y8g0LrRgNC40YLQtdGA0LjRjyDQnNCw0L3QvdCwLdCj0LjRgtC90LguINCi0L4g0LXRgdGC0Ywg0LTQu9GPINC90LjRhSDQvNGLINC90LjRh9C10LPQviDQvdC1INC80L7QttC10Lwg0YHQutCw0LfQsNGC0Ywg0L4g0YDQsNCy0LXQvdGB0YLQstC1INGA0LDRgdC/0YDQtdC00LXQu9C10L3QuNC5LgoKIyMgMTAuINCa0L7RjdGE0YTQuNGG0LjQtdC90YIg0J/QuNGA0YHQvtC90LAKCmBgYHtyfQpwYWlyd2lzZS5jb3IgPC0gZnVuY3Rpb24oZGF0YSwgbmFtZV9tZXRob2QgPSAicGVhcnNvbiIpewogIG0gPC0gY29yKGRhdGEpCiAgbS5wLnZhbHVlIDwtIGNvcihkYXRhKQogIAogIGZvciAoaSBpbiBjb2xuYW1lcyhkYXRhKSl7CiAgICBmb3IgKGogaW4gY29sbmFtZXMoZGF0YSkpewogICAgICBkYXRhLm91dCA8LSBkYXRhfD4gZmlsdGVyKCFpcy5uYShkYXRhW1tpXV0pICYgIWlzLm5hKGRhdGFbW2pdXSkpCiAgICAgIG1baSwgal0gPC0gY29yKHggPSBkYXRhLm91dFtbaV1dLCB5ID0gZGF0YS5vdXRbW2pdXSwgbWV0aG9kID0gbmFtZV9tZXRob2QpCiAgICAgIG0ucC52YWx1ZVtpLCBqXSA8LSBjb3IudGVzdCh4ID0gYXMudmVjdG9yKGRhdGEub3V0W1tpXV0pLCB5ID0gYXMudmVjdG9yKGRhdGEub3V0W1tqXV0pLCBtZXRob2QgPSBuYW1lX21ldGhvZCkkcC52YWx1ZQogICAgfQogIH0KICAKICBjb3JycGxvdChtLCBtZXRob2QgPSAibnVtYmVyIikKICBhcy5kYXRhLmZyYW1lKG0ucC52YWx1ZSkKfQpgYGAKCtCf0LXRgNC10LQg0LLRi9GH0LjRgdC70LXQvdC40LXQvCDQutC+0Y3RhNGE0LjRhtC40LXQvdGC0LAg0LrQvtGA0YDQtdC70Y/RhtC40Lgg0J/QuNGA0YHQvtC90LAg0L/QvtGB0LzQvtGC0YDQuNC8INC90LAg0L3QvtGA0LzQsNC70YzQvdC+0YHRgtGMINC/0YDQuNC30L3QsNC60L7QsiDQtNC70Y8g0LLRgdC10YUg0LjQvdC00LjQstC40LTQvtCyINC/0L4g0YLQtdGB0YLRgyDQqNCw0L/QuNGA0L4t0KPQuNC70LrQsDoKCmBgYHtyfQpkZi5zaGFwaXJvLnRlc3QuYWxsLnAudmFsdWUgPC0gZGF0YS5mcmFtZShCT0RZX1dFSV9QID0gMCwgQlJBSU5fV0VfUCA9IDAsIFNMT1dXQVZFX1AgPSAwLCBQQVJBRE9YX1AgPSAwLCBTTEVFUF9QID0gMCwgTElGRVNQQU5fUCA9IDAsIEdFU1RUSU1FX1AgPSAwKQoKZm9yIChpIGluIGMoMSkpIHsKICBkZi5zaGFwaXJvLnRlc3QuYWxsLnAudmFsdWVbMSwgXSA8LSBjKHNoYXBpcm8udGVzdChkZi5sb2cub3V0JEJPRFlfV0VJKSRwLnZhbHVlLCAKICBzaGFwaXJvLnRlc3QoZGYubG9nLm91dCRCUkFJTl9XRSkkcC52YWx1ZSwKICBzaGFwaXJvLnRlc3QoZGYubG9nLm91dCRTTE9XV0FWRSkkcC52YWx1ZSwKICBzaGFwaXJvLnRlc3QoZGYubG9nLm91dCRQQVJBRE9YKSRwLnZhbHVlLAogIHNoYXBpcm8udGVzdChkZi5sb2cub3V0JFNMRUVQKSRwLnZhbHVlLAogIHNoYXBpcm8udGVzdChkZi5sb2cub3V0JExJRkVTUEFOKSRwLnZhbHVlLAogIHNoYXBpcm8udGVzdChkZi5sb2cub3V0JEdFU1RUSU1FKSRwLnZhbHVlKQp9CgpkZi5zaGFwaXJvLnRlc3QuYWxsLnAudmFsdWUgfD4gcHJpbnRfZGYoKQpgYGAKCtCa0LDQuiDQstC40LTQuNC8INC/0L4gcC12YWx1ZSDQvNC+0LbQtdC8INGB0YfQuNGC0LDRgtGMLCDRh9GC0L4g0YDQsNGB0L/RgNC10LTQtdC70LXQvdC40Y8g0L/QvtGF0L7QttC4INC90LAg0L3QvtGA0LzQsNC70YzQvdGL0LUsINC/0L7RjdGC0L7QvNGDINC60YDQuNGC0LXRgNC40Lkg0L/RgNC+0LLQtdGA0LrQuCDQvdCwINC30L3QsNGH0LjQvNC+0YHRgtGMINC60L7RjdGE0YTQuNGG0LjQtdC90YLQsCDQutC+0YDRgNC10LvRj9GG0LjQuCDQsdGD0LTQtdGCINC/0YDQvtCy0LXRgNGP0YLRjCDQtdGJ0ZEg0Lgg0L3QtdC30LDQstC40YHQuNC80L7RgdGC0Ywg0L/RgNC40LfQvdCw0LrQvtCyLCDQsCDRgtCw0LrQttC1INC60L7RjdGE0YTQuNGG0LjQtdC90YIg0KHQv9C40YDQvNC10L3QsCDQsdGD0LTQtdGCINC/0YDQuNC80LXRgNC90L4g0YDQsNCy0LXQvSDQutC+0Y3RhNGE0LjRhtC40LXQvdGC0YMg0J/QuNGA0YHQvtC90LAuCgrQotC10L/QtdGA0Ywg0L/QvtGB0YfQuNGC0LDQtdC8INC60L7RjdGE0YTQuNGG0LjQtdC90YLRiyDQutC+0YDRgNC10LvRj9GG0LjQuCDQn9C40YDRgdC+0L3QsCDQtNC70Y8g0LLRgdC10YUg0L/QsNGAINC/0YDQuNC30L3QsNC60L7Qsiwg0L/RgNC40YfRkdC8INGD0LTQsNC70LXQvdC40LUgTkEg0LHRg9C00LXQvCDQv9GA0L7QuNC30LLQvtC00LjRgtGMINC/0L7Qv9Cw0YDQvdC+LCDRh9GC0L7QsdGLINC00LvRjyDQutCw0LbQtNC+0Lkg0L/QsNGA0Ysg0L/QvtC70YPRh9C40YLRjCDQvNCw0LrRgdC40LzQsNC70YzQvdC+0LUg0LrQvtC70LjRh9C10YHRgtCy0L4g0LjQvdC00LjQstC40LTQvtCyOgoKYGBge3J9CmRmLmxvZy5vdXQgfD4gCiAgbXV0YXRlKEJSQUlOX0JPRFkgPSBsb2coZXhwKEJSQUlOX1dFKSAvIGV4cChCT0RZX1dFSSkpKSB8PgogIGRwbHlyOjpzZWxlY3QoLU5BTUUsIC1FWFBfSU5ELCAtUFJFRF9JTkQsIC1EQU5HX0lORCkgfD4gCiAgcGFpcndpc2UuY29yKCkgfD4gCiAgcHJpbnRfZGYoKQpgYGAKCtCS0LjQtNC40LwsINGH0YLQviDQtNC70Y8g0LLRgdC10YUg0L/QsNGAINC/0YDQuNC30L3QsNC60L7QsiDQutGA0LjRgtC10YDQuNC5INC00LDRkdGCIHAtdmFsdWUg0LzQtdC90YzRiNC1ICRcYWxwaGEgPSAwLjA1JCwg0YLQviDQtdGB0YLRjCDQvtGC0LLQtdGA0LPQsNC10Lwg0LPQuNC/0L7RgtC10LfRgyDQviDQvdC10LfQvdCw0YfQuNC80L7RgdGC0Lgg0LfQsNCy0LjRgdC40LzRgdC+0YLQuCDQtNC70Y8g0Y3RgtC40YUg0L/RgNC40LfQvdCw0LrQvtCyLgoK0JfQsNCy0LjRgdC40LzQvtGB0YLQuDoKCjEuICoq0JLQtdGBINC80L7Qt9Cz0LAqKiDQt9Cw0LLQuNGB0LjRgiDQvtGCICoq0LLQtdGB0LAg0YLQtdC70LAqKjsKCjIuICoq0J7QsdGJ0LDRjyDQv9GA0L7QtNC+0LvQttC40YLQtdC70YzQvdC+0YHRgtGMINGB0L3QsCoqINC30LDQstC40YHQuNGCINC+0YIgKirQvNC10LTQu9C10L3QvdC+0LPQvioqINC4ICoq0LHRi9GB0YLRgNC+0LPQviDRgdC90LAqKiwg0LAg0YLQsNC60LbQtSDQvtC90Lgg0LfQsNCy0LjRgdGP0YIg0LTRgNGD0LMg0L7RgiDQtNGA0YPQs9CwLCDQvdC+INGB0LrQvtGA0LXQtSDQutC+0YHQstC10L3QvdC+LCDRgtCw0Log0LrQsNC6INGN0YLQviDRgdC+0L7RgtC90L7RiNC10L3QuNC1INGB0LrQvtGA0LXQtSDQuNC90LTQuNCy0LjQtNGD0LDQu9GM0L3QviDQtNC70Y8g0LrQsNC20LTQvtCz0L4g0LbQuNCy0L7RgtC90L7Qs9C+OwoKMy4gKirQktGA0LXQvNGPINCx0LXRgNC10LzQtdC90L3QvtGB0YLQuCoqINC30LDQstC40YHQuNGCINC+0YIgKirQv9GA0L7QtNC+0LvQttC40YLQtdC70YzQvdC+0YHRgtC4INC20LjQt9C90LgqKiwg0YLQsNC6INC60LDQuiDQsdC10YDQtdC80LXQvdC90L7RgdGC0Ywg0L3QtSDQtNC+0LvQttC90LAg0LfQsNC90LjQvNCw0YLRjCDQsdC+0LvRjNGI0LXQuSDRh9Cw0YHRgtC4INC20LjQt9C90Lgg0LjQty3Qt9CwINGD0Y/Qt9Cy0LjQvNC+0YHRgtC4INGC0LDQutC+0LPQviDQttC40LLQvtGC0L3QvtCz0L47Cgo0LiDQp9C10Lwg0LHQvtC70YzRiNC1INC20LjQstC+0YLQvdC+0LUsINGC0LXQvCDQtNC+0LvRjNGI0LUgKirQsdC10YDQtdC80LXQvdC90L7RgdGC0YwqKiDQuCAqKtC/0YDQvtC00L7Qu9C20LjRgtC10LvRjNC90L7RgdGC0Ywg0LbQuNC30L3QuCoqLCDRgtCw0Log0LrQsNC6INC60YDRg9C/0L3QvtC1INC20LjQstC+0YLQvdC+0LUg0YLRgNC10LHRg9C10YIg0LrQsNC6INCx0L7Qu9GM0YjQvtCz0L4g0YTQvtGA0LzQuNGA0L7QstCw0L3QuNGPINCy0L4g0LLRgNC10LzRjyDQsdC10YDQtdC80LXQvdC90L7RgdGC0LgsINGC0LDQuiDQutCw0Log0Lgg0LHQvtC70YzRiNC10LPQviDQstGA0LXQvNC10L3QuCDQstC30YDQvtGB0LvQtdC90LjRjzsKCjUuINCi0LDQutC20LUg0L7RgiDRgNCw0LfQvNC10YDQsCAqKtC80L7Qt9Cz0LAqKiDQt9Cw0LLQuNGB0LjRgiAqKtC/0YDQvtC00L7Qu9C20LjRgtC10LvRjNC90L7RgdGC0Ywg0LbQuNC30L3QuCoqLCDQvtC00L3QsNC60L4g0Y3RgtCwINC30LDQstC40YHQuNC80L7RgdGC0Ywg0L3QtSDRj9Cy0LvRj9C10YLRgdGPINC/0YDRj9C80L7QuSwg0YLQsNC6INC60LDQuiDQtdGB0LvQuCDQvNGLINGA0LDRgdGB0LzQvtGC0YDQuNC8INC00L7Qu9GOICoq0LLQtdGB0LAg0LzQvtC30LPQsCoqINC6ICoq0LLQtdGB0YMg0YLQtdC70LAqKiwg0YLQviDQutC+0YDRgNC10LvRj9GG0LjRjyDQt9C90LDRh9C40YLQtdC70YzQvdC+INGD0L/QsNC00ZHRgjsKCjYuINCX0LDQstC40YHQuNC80L7RgdGC0Lgg0LzQtdC20LTRgyAqKtGB0L3QvtC8Kiog0LggKirQstC10YHQvtC8INGC0LXQu9CwKiosICoq0YHQvdC+0LwqKiDQuCAqKtC/0YDQvtC00L7Qu9C20LjRgtC10LvRjNC90L7RgdGC0YzRjiDQttC40LfQvdC4Kiog0LjQu9C4ICoq0YHQvdC+0LwqKiDQuCAqKtCy0YDQtdC80LXQvdC4INCx0LXRgNC10LzQtdC90L3QvtGB0YLQuCoqINC00L7RgdGC0LDRgtC+0YfQvdC+INGB0LvQvtC20L3QviDQvtCx0YrRj9GB0L3QuNGC0YwsINC00LvRjyDRjdGC0L7Qs9C+INC90YPQttC90L4g0YPQs9C70YPQsdC70Y/RgtGB0Y8g0LIg0YHQu9C+0LbQvdGL0LUg0LHQuNC+0LvQvtCz0LjRh9C10YHQuNC60LUg0L/RgNC+0YbQtdGB0YHRiy4KCiMjIDExLiDQmtC+0Y3RhNGE0LjRhtC40LXQvdGCINCh0L/QuNGA0LzQtdC90LAKCtCf0YDQvtCy0LXRgNC40LwsINGH0YLQviDRgNCw0L3Qs9C+0LLRi9C5INC60L7RjdGE0YTQuNGG0LjQtdC90YIg0LrQvtGA0YDQtdC70Y/RhtC40Lgg0KHQv9C40YDQvNC10L3QsCDQv9GA0LjQvNC10YDQvdC+INGA0LDQstC10L0g0J/QuNGA0YHQvtC90YM6CgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpkZi5sb2cub3V0IHw+IAogZHBseXI6OnNlbGVjdCgtTkFNRSwgLUVYUF9JTkQsIC1QUkVEX0lORCwgLURBTkdfSU5EKSB8PiAKICBwYWlyd2lzZS5jb3IobmFtZV9tZXRob2QgPSAic3BlYXJtYW4iKSB8PiAKICBwcmludF9kZigpCmBgYAoK0KDQtdC30YPQu9GM0YLQsNGC0Ysg0YHRhdC+0LbQuCDQuCDRjdGC0L4g0L3QtSDRg9C00LjQstC40YLQtdC70YzQvdC+LCDQstC10LTRjCDQvNGLINGB0YfQuNGC0LDQtdC8LCDRh9GC0L4g0LTQsNC90L3Ri9C1INC/0L7RhdC+0LbQuCDQvdCwINC90L7RgNC80LDQu9GM0L3Ri9C1LCDQsCDQtNC70Y8g0L3QuNGFINC+0YLQu9C40YfQuNC1INC60L7RjdGE0YTQuNGG0LjQtdC90YLQsCDQn9C40YDRgdC+0L3QsCDQvtGCINCh0L/QuNGA0LzQtdC90LAg0L3QtdC30L3QsNGH0LjRgtC10LvRjNC90L4uCgojIyAxMi4g0KfQsNGB0YLQvdGL0LUg0LrQvtGA0YDQtdC70Y/RhtC40L7QvdC90YvQtSDQvtGC0L3QvtGI0LXQvdC40Y8KCtCe0LTQvdCw0LrQviDQstC+0LfQvdC40LrQsNC10YIg0L/QvtC00L7Qt9GA0LXQvdC40LUsINGH0YLQviDQvNC90L7Qs9C40LUg0LfQsNCy0LjRgdC40LzQvtGB0YLQuCDQuNC80LXRjtGCINC00YDRg9Cz0YPRjiDQv9GA0LjRh9C40L3Rgywg0L3QtdC20LXQu9C4INC30LDQstC40YHQuNC80L7RgdGC0Ywg0YLQvtC70YzQutC+INC00YDRg9CzINC+0YIg0LTRgNGD0LPQsC4g0JrQsNC6INCx0YvQu9C+INCy0LjQtNC90L4g0L/QviDRj9GJ0LjQutCw0Lwg0YEg0YPRgdCw0LzQuCDQtNC70Y8gKirQuNC90LTQtdC60YHQsCDQvtC/0LDRgdC90L7RgdGC0Lgg0LzQtdGB0YLQsCDQtNC70Y8g0YHQvdCwKiog0LXRgdGC0Ywg0LzQvtC90L7RgtC+0L3QvdCw0Y8g0LfQsNCy0LjRgdC40LzQvtGB0YLRjCDQv9C+0YfRgtC4INCy0YHQtdGFINC/0YDQuNC30L3QsNC60L7QsiDQvtGCINGN0YLQvtCz0L4g0LjQvdC00LXQutGB0LAuINCf0L7RjdGC0L7QvNGDINCy0L7Qt9C90LjQutCw0LXRgiDQuNC00LXRjyDQv9GA0L7QstC10YDQutC4INC60L7RjdGE0YTQuNGG0LjQtdC90YLQsCDRh9Cw0YHRgtC90L7QuSDQutC+0YDRgNC10LvRj9GG0LjQuCDRgSDQstGL0YfQtdGC0L7QvCDQstC70LjRj9C90LjRjyDRjdGC0L7Qs9C+INC40L3QtNC10LrRgdCwLgoKYGBge3J9CmRmLmxvZy5vdXQgfD4gCiAgZ3JvdXBfYnkoRVhQX0lORCkgfD4gCiAgbXV0YXRlKEJPRFlfV0VJID0gQk9EWV9XRUkgLSBtZWFuKEJPRFlfV0VJLCBuYS5ybSA9IFRSVUUpLCBCUkFJTl9XRSA9IEJSQUlOX1dFIC0gbWVhbihCUkFJTl9XRSwgbmEucm0gPSBUUlVFKSwgU0xPV1dBVkUgPSBTTE9XV0FWRSAtIG1lYW4oU0xPV1dBVkUsIG5hLnJtID0gVFJVRSksIFBBUkFET1ggPSBQQVJBRE9YIC0gbWVhbihQQVJBRE9YLCBuYS5ybSA9IFRSVUUpLCBTTEVFUCA9IFNMRUVQIC0gbWVhbihTTEVFUCwgbmEucm0gPSBUUlVFKSwgTElGRVNQQU4gPSBMSUZFU1BBTiAtIG1lYW4oTElGRVNQQU4sIG5hLnJtID0gVFJVRSksIEdFU1RUSU1FID0gR0VTVFRJTUUgLSBtZWFuKEdFU1RUSU1FLCBuYS5ybSA9IFRSVUUpKSB8PgogIGdyb3VwX2J5KC5kcm9wID0gVFJVRSkgfD4gZHBseXI6OnNlbGVjdCgtTkFNRSwgLUVYUF9JTkQsIC1QUkVEX0lORCwgLURBTkdfSU5EKSB8PiAKICBwYWlyd2lzZS5jb3IoKSB8PiAKICBwcmludF9kZigpCmBgYAoK0JfQtNC10YHRjCDQvNGLINCy0LjQtNC40LwsINGH0YLQviDQtNC+0YHRgtCw0YLQvtGH0L3QviDQvNC90L7Qs9C+INC30LDQstC40YHQuNC80L7RgdGC0LXQuSDQv9C10YDQtdGB0YLQsNC70Lgg0LHRi9GC0Ywg0LfQvdCw0YfQuNC80YvQvNC4ICgkXGFscGhhID0gMC4wNSQpLCDRgtCw0Log0LrQsNC6INC80Ysg0LjRgdC60LvRjtGH0LjQu9C4INCy0LvQuNGP0L3QuNC1ICoq0LjQvdC00LXQutGB0LAg0L7Qv9Cw0YHQvdC+0YHRgtC4INC80LXRgdGC0LAg0LTQu9GPINGB0L3QsCoqLiDQntC20LjQtNCw0LXQvNC+INC30LDQstC40YHQuNC80L7RgdGC0Lgg0LLRgdC10YUg0LLQuNC00L7QsiAqKtGB0L3QsCoqINC+0YIgKirQstC10YHQsCDRgtC10LvQsCoqLCAqKtCy0LXRgdCwINC80L7Qt9Cz0LAqKiDQuCAqKtC/0YDQvtC00L7Qu9C20LjRgtC10LvRjNC90L7RgdGC0Lgg0YHQvdCwKiog0LfQvdCw0YfQuNGC0LXQu9GM0L3QviDQvtGB0LvQsNCx0LvQuCwg0L7QtNC90LDQutC+INC30LDQstC40YHQuNC80L7RgdGC0Lgg0YHQvdCwINC+0YIgKirQstGA0LXQvNC10L3QuCDQsdC10YDQtdC80LXQvdC90L7RgdGC0LgqKiDRg9C80LXQvdGM0YjQuNC70LjRgdGMINC80LDQu9C+LgoKIyMgMTMuINCc0L3QvtC20LXRgdGC0LLQtdC90L3QsNGPINGA0LXQs9GA0LXRgdGB0LjRjwoK0J/QvtGB0LzQvtGC0YDQuNC8INC90LAg0LTQsNC90L3Ri9C1OgoKYGBge3Igd2FybmluZz1GQUxTRX0KZGYubG9nLm91dCB8PiBkcGx5cjo6c2VsZWN0KC1OQU1FKSB8PiBnZ3BhaXJzKGNvbHVtbnMgPSAxOjcpCmBgYAoK0JLRgdGRLCDRh9GC0L4g0L3Rg9C20L3Qviwg0LHRi9C70L4g0L/RgNC+0LvQvtCz0LDRgNC40YTQvNC40YDQvtCy0LDQvdC+INGA0LDQvdGM0YjQtSwg0L/QvtGN0YLQvtC80YMg0L/RgNC40YHRgtGD0L/QuNC8INC6INGA0LXQs9GA0LXRgdGB0LjQuC4g0KXQvtGC0LjQvCDQv9GA0L7Qs9C90L7Qt9C40YDQvtCy0LDRgtGMINCy0YDQtdC80Y8g0LbQuNC30L3QuCDQv9C+INC+0YHRgtCw0LvRjNC90YvQvCDQtNCw0L3QvdGL0LwuCgrQodGC0YDQvtC40Lwg0YDQtdCz0YDQtdGB0YHQuNGOINC/0L4g0LLRgdC10Lwg0L/RgNC40LfQvdCw0LrQsNC8OgoKYGBge3J9CmRmLmxvZy5vdXQubmEgPC0gZGYubG9nLm91dCB8PiBkcGx5cjo6ZmlsdGVyKCFpcy5uYShTTE9XV0FWRSkgJiAhaXMubmEoUEFSQURPWCkgJiAhaXMubmEoU0xFRVApICYgIWlzLm5hKExJRkVTUEFOKSAmICFpcy5uYShHRVNUVElNRSkpCm1vZGVsIDwtIGxtKExJRkVTUEFOIH4gLiwgZGYubG9nLm91dC5uYSB8PiBkcGx5cjo6c2VsZWN0KC1OQU1FKSkKbW9kZWwuYmV0YSA8LSBsbS5iZXRhKG1vZGVsKQpzdW1tYXJ5KG1vZGVsLmJldGEpCmBgYAoK0KDQtdCz0YDQtdGB0YHQuNGPINC30L3QsNGH0LjQvNCwLCDQvdC+INC30L3QsNGH0LjQvNGL0YUg0LrQvtGN0YTRhNC40YbQuNC10L3RgtC+0LIg0L3QsCDRg9GA0L7QstC90LUgJFxhbHBoYSA9IDAuMDUkINGC0L7Qu9GM0LrQviDQtNCy0LA6INC00LvRjyAqKtCy0LXRgdCwINC80L7Qt9Cz0LAqKiDQuCDQtNC70Y8g0YTQuNC60YLQuNCy0L3QvtC5INC/0LXRgNC10LzQtdC90L3QvtC5ICoq0LjQvdC00LXQutGB0LAg0YXQuNGJ0L3QuNGH0LXRgdGC0LLQsCoqINC/0YDQuCDQt9C90LDRh9C10L3QuNC4ICQzJC4KCiMjIDE0LiDQlNC+0LLQtdGA0LjRgtC10LvRjNC90YvQtSDRjdC70LvQuNC/0YHRiwoK0J/QvtGB0YfQuNGC0LDQtdC8INC60L7QstCw0YDQuNCw0YbQuNC+0L3QvdGD0Y4g0Lgg0LrQvtGA0YDQtdC70Y/RhtC40L7QvdC90YPRjiDQvNCw0YLRgNC40YbRgyDQtNC70Y8g0LrQvtGN0YTRhNC40YbQuNC10L3RgtC+0LIg0YDQtdCz0YDQtdGB0YHQuNC4OgoKYGBge3J9CmR1bW15LnZhcmlhYmxlIDwtIGRhdGEuZnJhbWUoTkFNRSA9IGRmLmxvZy5vdXQubmEkTkFNRSkKCmZvciAoaSBpbiAyOjUpewogIGR1bW15LnZhcmlhYmxlW1twYXN0ZSgiUFJFRF9JTkQiLCBpLCBzZXAgPSAiXyIpXV0gPSBpZmVsc2UoZGYubG9nLm91dC5uYSRQUkVEX0lORCA9PSBpLCAxLCAwKQp9CmZvciAoaSBpbiAyOjUpewogIGR1bW15LnZhcmlhYmxlW1twYXN0ZSgiRVhQX0lORCIsIGksIHNlcCA9ICJfIildXSA9IGlmZWxzZShkZi5sb2cub3V0Lm5hJEVYUF9JTkQgPT0gaSwgMSwgMCkKfQpmb3IgKGkgaW4gMjo1KXsKICBkdW1teS52YXJpYWJsZVtbcGFzdGUoIkRBTkdfSU5EIiwgaSwgc2VwID0gIl8iKV1dID0gaWZlbHNlKGRmLmxvZy5vdXQubmEkREFOR19JTkQgPT0gaSwgMSwgMCkKfQoKbi5kZiA8LSBsZW5ndGgoZGYubG9nLm91dC5uYSROQU1FKQpzaWdtYS5kZiA8LSBzdW0obW9kZWwkcmVzaWR1YWxzICoqIDIpIC8gKG4uZGYgLSBtb2RlbCRyYW5rKQpjb3ZNYXRyaXguZGYgPC0gZGYubG9nLm91dC5uYSB8PiBkcGx5cjo6c2VsZWN0KC1OQU1FLCAtUFJFRF9JTkQsIC1FWFBfSU5ELCAtREFOR19JTkQpIHw+IGNiaW5kKGR1bW15LnZhcmlhYmxlIHw+IGRwbHlyOjpzZWxlY3QoLU5BTUUpKSB8PiBjb3YoKQpjb3ZfYi5kZiA8LSBzaWdtYS5kZiAvIG4uZGYgKiBzb2x2ZShjb3ZNYXRyaXguZGYpCmNvdl9iZXRhLmRmIDwtIHNpZ21hLmRmIC8gbi5kZiAqIHNvbHZlKGNvdjJjb3IoY292TWF0cml4LmRmKSkKY29yX2IuZGYgPC0gY292MmNvcihjb3ZfYi5kZikKY29ycnBsb3QoY29yX2IuZGYsIG1ldGhvZCA9ICJjb2xvciIpCmBgYAoK0JLQuNC00LjQvCDRgdC40LvRjNC90YPRjiDQutC+0YDRgNC10LvRj9GG0LjRjiDQvNC10LbQtNGDINC80LXQttC00YMg0L7RhtC10L3QutCw0LzQuCDQutC+0Y3RhNGE0LjRhtC40LXQvdGC0L7QsiDRgNC10LPRgNC10YHRgdC40Lgg0LzQtdC20LTRgyAqKtCy0LXRgdC+0Lwg0YLQtdC70LAqKiDQuCAqKtC80L7Qt9Cz0LAqKiwg0LzQtdC20LTRgyDQstGB0LXQvNC4INCy0LjQtNCw0LzQuCAqKtGB0L3QsCoqLCDQvNC10LbQtNGDICoq0LjQvdC00LXQutGB0LDQvNC4INC+0LHRidC10Lkg0L7Qv9Cw0YHQvdC+0YHRgtC4Kiog0LggKirRhdC40YnQvdC40YfQtdGB0YLQstCwKiosINCwINGC0LDQutC20LUg0LzQtdC20LTRgyAqKtCy0LXRgdC+0Lwg0LzQvtC30LPQsCoqINC4ICoq0L/RgNC+0LTQvtC70LbQuNGC0LXQu9GM0L3QvtGB0YLRjNGOINC20LjQt9C90LgqKi4KCtCf0L7RgdC80L7RgtGA0LjQvCDQvdCwINC00L7QstC10YDQuNGC0LXQu9GM0L3Ri9C5INGN0LvQu9C40L/RgdC+0LjQtCDQtNC70Y8g0LfQvdCw0YfQuNC80YvRhSDQutC+0Y3RhNGE0LjRhtC40LXQvdGC0L7Qsiwg0L7QsdC+0LfQvdCw0YfQtdC90L3Ri9GFINCy0YvRiNC1OgoKYGBge3J9CnBsb3QoZWxsaXBzZTo6ZWxsaXBzZShjb3ZfYmV0YS5kZltjKDIsIDkpLCBjKDIsIDkpXSwgY2VudHJlID0gbW9kZWwuYmV0YSRzdGFuZGFyZGl6ZWQuY29lZmZpY2llbnRzW2MoMywgOSldLCBsZXZlbD0wLjk1LCBucG9pbnRzID0gMTAwKSwgdHlwZSA9ICJsIiwgYXNwID0gMSkKbGluZXMoeCA9IGMoLTIsIDIpLCB5ID0gYygyLCAtMiksIGNvbCA9ICJyZWQiKQpgYGAKCtCe0L0g0LjQvNC10LXRgiDQv9C70L7RhdC+0Lkg0LLQuNC0OiDQu9C40LHQviDQvtCx0LAg0LjQvNC10LXRgiDRgdC40LvRjNC90L7QtSDQstC70LjRj9C90LjQtSwg0LvQuNCx0L4g0L7QsdCwIC0tLSDRgdC70LDQsdC+0LUuINCf0YDQuNGH0ZHQvCAqKtC40L3QtNC10LrRgSDRhdC40YnQvdC40YfQtdGB0YLQstCwKiog0LzQvtC20LXRgiDQsdGL0YLRjCDRgdC+0LLRgdC10Lwg0L3QtSDQt9C90LDRh9C40LwsINGC0LDQuiDQutCw0Log0Y3Qu9C70LjQv9GBINC/0LXRgNC10YHQtdC60LDQtdGCINC90YPQu9C10LLQvtC1INC30L3QsNGH0LXQvdC40LUg0L/QviAkeSQuCgojIyAxNS4g0KHQuNC70YzQvdC+INC60L7RgNGA0LXQu9C40YDRg9GO0YnQuNC1INC/0YDQuNC30L3QsNC60LgKCtCf0L7RgdGC0YDQvtC40Lwg0YLQsNCx0LvQuNGG0YMgVklGOgoKYGBge3J9Cm9sc192aWZfdG9sKG1vZGVsKQpgYGAKCtCd0LDQsdC70Y7QtNCw0LXQvCDQvtGH0LXQvdGMINCx0L7Qu9GM0YjRg9GOINC80YPQu9GM0YLQuNC60L7Qu9C70LjQvdC10LDRgNC90L7RgdGC0Ywg0L/QvtGH0YLQuCDQv9C+INCy0YHQtdC8INC/0YDQuNC30L3QsNC60LDQvC4KCtCi0LXQv9C10YDRjCDRgtCw0LHQu9C40YbQsCDRgSDRh9Cw0YHRgtC40YfQvdGL0LzQuCDQutC+0YDRgNC10LvRj9GG0LjRj9C80Lg6CgpgYGB7cn0Kb2xzX2NvcnJlbGF0aW9ucyhtb2RlbCkKYGBgCgrQodC40LvRjNC90L7QtSDQstC70LjRj9C90LjQtSDQvdCwINC30LDQstC40YHQuNC80YPRjiDQv9C10YDQtdC80LXQvdC90YPRjiDQt9CwINC40YHQutC70Y7Rh9C10L3QuNC10Lwg0L7RgdGC0LDQu9GM0L3Ri9GFINC+0LrQsNC30YvQstCw0Y7Rgiwg0LIg0L/QtdGA0YPRjiDQvtGH0LXRgNC10LTRjCwgKirQstC10YEg0LzQvtC30LPQsCoqLCDQsCDRgtCw0LrQttC1ICoq0LjQvdC00LXQutGBINGF0LjRidC90LjRh9C10YHRgtCy0LAqKiDQuCAqKtC+0LHRidC40Lkg0LjQvdC00LXQutGBINC+0L/QsNGB0L3QvtGB0YLQuCoqICjRhdC+0YLRjyDQvtC9INGB0LjQu9GM0L3QviDQt9Cw0LLQuNGB0LjRgiDQvtGCICoq0LjQvdC00LXQutGB0LAg0YXQuNGJ0L3QuNGH0LXRgdGC0LLQsCoqKS4KCtCf0L7Qv9GA0L7QsdGD0LXQvCDRg9Cx0YDQsNGC0Ywg0YHQuNC70YzQvdC+INC60L7RgNGA0LXQu9C40YDRg9GO0YnQuNC1LiDQo9Cx0LXRgNGR0LwgKirQstC10YEg0YLQtdC70LAqKiwgKirQvtCx0YnQtdC1INCy0YDQtdC80Y8g0YHQvdCwKiog0LggKirQvtCx0YnQuNC5INC40L3QtNC10LrRgSDQvtC/0LDRgdC90L7RgdGC0LgqKjoKCmBgYHtyfQptb2RlbC5taW51c2NvciA8LSBsbShMSUZFU1BBTiB+IC4sIGRmLmxvZy5vdXQubmEgfD4gZHBseXI6OnNlbGVjdCgtTkFNRSwgLUJPRFlfV0VJLCAtIFNMRUVQLCAtREFOR19JTkQpKQptb2RlbC5taW51c2Nvci5iZXRhIDwtIGxtLmJldGEobW9kZWwubWludXNjb3IpCnN1bW1hcnkobW9kZWwubWludXNjb3IuYmV0YSkKYGBgCtCf0L4g0LfQvdCw0YfQuNC80L7RgdGC0Lgg0LjQvNC10LXQvCDRgtGDINC20LUg0YHQuNGC0YPQsNGG0LjRjiwg0L7QtNC90LDQutC+INC30L3QsNGH0LjQvNC+0YHRgtGMINC/0L4gcC12YWx1ZSDQv9C+0LTRgNC+0YHQu9CwLCDQsCAkUl4yJCDQv9C+0YfRgtC4INC90LUg0LjQt9C80LXQvdC40LvRgdGPLgoK0JzRg9C70YzRgtC40LrQvtC70LvQuNC90LXQsNGA0L3QvtGB0YLRjCDQuNGB0YfQtdC30LvQsDoKCmBgYHtyfQpvbHNfdmlmX3RvbChtb2RlbC5taW51c2NvcikKYGBgCgojIyAxNi4g0J/QvtGI0LDQs9C+0LLQsNGPINGA0LXQs9GA0LXRgdGB0LjRjwoKIyMjIEJhY2t3YXJkCgrQn9C+0YHRgtGA0L7QuNC8INC/0L7RiNCw0LPQvtCy0YPRjiDRgNC10LPRgNC10YHRgdC40Y4uINCh0L3QsNGH0LDQu9CwINGA0LDRgdGB0LzQvtGC0YDQuNC8INGD0LHQsNCy0LvQtdC90LjQtSDQutC+0LvQuNGH0LXRgdGC0LLQsCDQv9Cw0YDQsNC80LXRgtGA0L7QsjoKCmBgYHtyfQpzdC5idy5sbSA8LSBvbHNfc3RlcF9iYWNrd2FyZF9wKG1vZGVsLm1pbnVzY29yKQpzdC5idy5sbQpgYGAKCtCa0LDQuiDRjdGC0L4g0LLRi9Cz0LvRj9C00LjRgiDQv9C+INGI0LDQs9Cw0Lw6CgpgYGB7cn0KcGxvdChzdC5idy5sbSkKYGBgCgrQmtCw0LrQsNGPINC20LUg0LzQvtC00LXQu9GMINC/0L7Qu9GD0YfQsNC10YLRgdGPINCyINC40YLQvtCz0LU6CgpgYGB7cn0Kc3VtbWFyeShzdC5idy5sbSRtb2RlbCkKYGBgCgrQntGB0YLQsNC70L7RgdGMICQyJCDQv9GA0LjQt9C90LDQutCwLCDRg9C00LDQu9C40LvQuNGB0YwgKirQstGA0LXQvNGPINCx0LXRgNC10LzQtdC90L3QvtGB0YLQuCoqLCAqKtCx0YvRgdGC0YDRi9C5INGB0L7QvSoqLCAqKtC80LXQtNC70LXQvdC90YvQuSDRgdC+0L0qKiDQuCAqKtC40L3QtNC10LrRgSDQstC+0LfQtNC10LnRgdGC0LLQuNGPINGB0L3QsCoqLiAKCiMjIyBGb3J3YXJkCgrQotC10L/QtdGA0Ywg0LHRg9C00LXQvCDQtNC+0LHQsNCy0LvRj9GC0Ywg0L/RgNC40LfQvdCw0LrQuDoKCmBgYHtyfQpzdC5mdy5sbSA8LSBvbHNfc3RlcF9mb3J3YXJkX3AobW9kZWwubWludXNjb3IpCnN0LmZ3LmxtCmBgYAoK0JrQsNC6INGN0YLQviDQstGL0LPQu9GP0LTQtdC70L4g0L3QsCDQs9GA0LDRhNC40LrQtToKCmBgYHtyfQpwbG90KHN0LmZ3LmxtKQpgYGAKCtCa0LDQutGD0Y4g0LbQtSDQvNC+0LTQtdC70Ywg0L/QvtC70YPRh9C40LvQuDoKCmBgYHtyfQpzdW1tYXJ5KHN0LmZ3LmxtJG1vZGVsKQpgYGAKCtCe0L3QsCDQsNC90LDQu9C+0LPQuNGH0L3QsCDRgtC+0LksINC60L7RgtC+0YDRg9GOINC/0L7Qu9GD0YfQuNC70Lgg0L7RgiBiYWNrd2FyZC4KCtCe0LTQvdCw0LrQviDRgtC+0LvRjNC60L4g0L/RgNC4INC+0LTQvdC+0Lwg0LfQvdCw0YfQtdC90LjQuCAqKtC40L3QtNC10LrRgdCwINGF0LjRidC90LjRh9C10YHRgtCy0LAqKiDQutC+0Y3RhNGE0LjRhtC40LXQvdGCINC30L3QsNGH0LjQvNGL0LksINC/0L7RjdGC0L7QvNGDINCy0YDRg9GH0L3Rg9GOINGB0L7Qt9C00LDQtNC40Lwg0YTQuNC60YLQuNCy0L3Ri9C1INC/0LXRgNC10LzQtdC90L3Ri9C1INC4INC/0YDQvtCy0LXQtNGR0Lwg0L7Qv9GP0YLRjCDQsNCy0YLQvtC80LDRgtC40YfQtdGB0LrQuNC5INC+0YLQsdC+0YA6CgpgYGB7cn0KZGYubG9nLm91dC5kdW1teSA8LSBkZi5sb2cub3V0Lm5hCmZvciAoaSBpbiAyOjUpewogIGRmLmxvZy5vdXQuZHVtbXlbW3Bhc3RlKCJQUkVEX0lORCIsIGksIHNlcCA9ICJfIildXSA8LSBpZmVsc2UoZGYubG9nLm91dC5kdW1teSRQUkVEX0lORCA9PSBpLCAxLCAwKQp9Cm1vZGVsLm91dC5kdW1teSA8LSBsbShMSUZFU1BBTiB+IEJSQUlOX1dFICsgUFJFRF9JTkRfMiArIFBSRURfSU5EXzMgKyBQUkVEX0lORF80ICsgUFJFRF9JTkRfNSwgZGF0YSA9IGRmLmxvZy5vdXQuZHVtbXkpCnN0LmJ3LmxtLmR1bW15IDwtIG9sc19zdGVwX2JhY2t3YXJkX3AobW9kZWwub3V0LmR1bW15KQpwbG90KHN0LmJ3LmxtLmR1bW15KQpzdW1tYXJ5KHN0LmJ3LmxtLmR1bW15JG1vZGVsKQpgYGAKCtCe0YHRgtCw0LvQvtGB0Ywg0LTQstCwINC30L3QsNGH0LXQvdC40Y8gKirQuNC90LTQtdC60YHQsCDRhdC40YnQvdC40YfQtdGB0YLQstCwKiouCgojIyAxNy4g0J7RgdGC0LDRgtC60Lgg0LggUHJlZGljdGVkIHZzIFJlc2lkdWFscwoK0J3QsNGI0LvQuCDQu9GD0YfRiNGD0Y4g0LzQvtC00LXQu9GMINGA0LXQs9GA0LXRgdGB0LjQuC4g0JTQvtCx0LDQstC40Lwg0YLQtSDQvdCw0LHQu9GO0LTQtdC90LjRjywg0LrQvtGC0L7RgNGL0LUg0LjQvNC10LvQuCDQv9GA0L7Qv9GD0YnQtdC90L3Ri9C1INC30L3QsNGH0LXQvdC40Y8g0YLQvtC70YzQutC+INC90LAg0YPQsdGA0LDQvdC90YvRhSDQtNCw0L3QvdGL0YU6CgpgYGB7cn0KZGYubG9nLm5hLmJlc3QgPC0gZGYubG9nIHw+IGZpbHRlcighaXMubmEoTElGRVNQQU4pKQpmb3IgKGkgaW4gMjo1KXsKICBkZi5sb2cubmEuYmVzdFtbcGFzdGUoIlBSRURfSU5EIiwgaSwgc2VwID0gIl8iKV1dIDwtIGlmZWxzZShkZi5sb2cubmEuYmVzdCRQUkVEX0lORCA9PSBpLCAxLCAwKQp9Cm1vZGVsLmJlc3QgPC0gbG0oTElGRVNQQU4gfiBCUkFJTl9XRSArIFBSRURfSU5EXzIgKyBQUkVEX0lORF8zLCBkYXRhID0gZGYubG9nLm5hLmJlc3QpCm1vZGVsLmJldGEuYmVzdCA8LSBsbS5iZXRhKG1vZGVsLmJlc3QpCnN1bW1hcnkobW9kZWwuYmV0YS5iZXN0KQpgYGAKCtCv0YHQvdC+LCDRh9GC0L4g0YHQvtCz0LvQsNGB0L7QstCw0L3QvdC+0YHRgtGMINC80L7QtNC10LvQuCDRg9C/0LDQu9CwLCDRgtCw0Log0LrQsNC6INC80Ysg0L/QvtGB0YLRgNC+0LjQu9C4INC10ZEg0L3QsCDQsdC+0LvRjNGI0LXQvCDQvdCw0LHQvtGA0LUg0LTQsNC90L3Ri9GFICjQv9C70Y7RgSDQsdGL0LvQuCDQtNC+0LHQsNCy0LvQtdC90Ysg0LLRi9Cx0YDQvtGB0YssINGD0LTQsNC70LXQvdC90YvQtSDRgNCw0L3RjNGI0LUpLgoK0KLQtdC/0LXRgNGMINC/0L7RgdC80L7RgtGA0LjQvCDQvdCwINC90L7RgNC80LDQu9GM0L3QvtGB0YLRjCDQvtGB0YLQsNGC0LrQvtCyICjRhdC+0YLQuNC8INGC0L7Rh9C90L7RgdGC0Ywg0L/RgNC+0LLQtdGA0Y/QtdC80YvRhSDQutGA0LjRgtC10YDQuNC10LIpOgoKYGBge3J9CnBsb3QobW9kZWwuYmVzdCwgd2hpY2g9MikKYGBgCgrQktC40LTQuNC8LCDRh9GC0L4g0YHQv9GA0LDQstCwINC90L7RgNC80LDQu9GM0L3QvtGB0YLQuCDQvdC1INC90LDQsdC70Y7QtNCw0LXRgtGB0Y8sINGF0L7RgtGPINC90LAg0L7RgdC90L7QstC90L7QvCDQvNCw0YHRgdC40LLQtSDQtNCw0L3QvdGL0YUg0L3QvtGA0LzQsNC70YzQvdC+0YHRgtGMINC/0YDQuNGB0YPRgtGB0YLQstGD0LXRgi4KCtCi0LXQv9C10YDRjCDQv9C+0YHQvNC+0YLRgNC40Lwg0L3QsCDQs9GA0LDRhNC40LogUHJlZGljdGVkIHZzIFJlc2lkdWFsczoKCmBgYHtyfQpwbG90KG1vZGVsLmJlc3Qsd2hpY2g9MSkKYGBgCgrQktC40LTQuNC8INC00L7RgdGC0LDRgtC+0YfQvdC+INGA0LDQstC90L7QvNC10YDQvdC+0LUg0YDQsNGB0L/RgNC10LTQtdC70LXQvdC40LUg0LIg0L/RgNGP0LzQvtGD0LPQvtC70YzQvdC+0Lkg0L7QsdC70LDRgdGC0Lgg0L7QutC+0LvQviDQvdGD0LvRjy4g0KLQviDQtdGB0YLRjCDQvNC+0LbQvdC+INC/0YDQtdC00L/QvtC70L7QttC40YLRjCDQsNC00LXQutCy0LDRgtC90L7RgdGC0Ywg0LLRi9Cx0YDQsNC90L3QvtC5INC70LjQvdC10LnQvdC+0Lkg0LzQvtC00LXQu9C4LCDQsCDRgtCw0LrQttC1INCz0L7QvNC+0LPQtdC00L7RgdGC0LjRh9C90L7RgdGC0YwgKNGC0L4g0LXRgdGC0Ywg0YDQsNCy0LXQvdGB0YLQstC+INC00LjRgdC/0LXRgNGB0LjQuSDQvtGB0YLQsNGC0LrQvtCyKS4KCtCT0YDQsNGE0LjQuiBSZXNpZHVscyB2cyBEZWxldGVkIFJlc2lkdWFsczoKCmBgYHtyfQpkZWxfcmVzIDwtIHNhcHBseSgxOjU4LCBmdW5jdGlvbihuKSBkZi5sb2cubmEuYmVzdCRMSUZFU1BBTltuXSAtIHByZWRpY3QobG0oTElGRVNQQU4gfiBCUkFJTl9XRSArIFBSRURfSU5EXzIgKyBQUkVEX0lORF8zLCBkYXRhID0gZGYubG9nLm5hLmJlc3RbLW4sIF0pLCBkZi5sb2cubmEuYmVzdFtuLCBdKSkKcGxvdCh4ID0gbW9kZWwuYmVzdCRyZXNpZHVhbHMsIHkgPSBkZWxfcmVzLCB4bGFiID0gIlJzaWR1YWxzIiwgeWxhYiA9ICJEZWxldGVkIHJlc2lkdWFscyIpCmxpbmVzKHggPSBjKC0zLCAzKSwgeSA9IGMoLTMsIDMpKQphYmxpbmUobG0oZGVsX3JlcyB+IG1vZGVsLmJlc3QkcmVzaWR1YWxzKSwgY29sID0gImJsdWUiKQp0ZXh0KHggPSBtb2RlbC5iZXN0JHJlc2lkdWFscywgeSA9IGRlbF9yZXMsIGxhYmVscyA9IHJvd25hbWVzKGRmLmxvZy5uYS5iZXN0KSwgY2V4ID0gMC42LCBwb3MgPSA0LCBjb2wgPSAicmVkIikKYGBgCgrQmtCw0Log0Lgg0L7QttC40LTQsNC70L7RgdGMLCDRgtC+0YfQutC4INGA0LDRgdC/0L7Qu9C+0LbQuNC70LjRgdGMINC/0L7QtCDRh9GD0YLRjCDQsdC+0LvRjNGI0LjQvCDQvdCw0LrQu9C+0L3QvtC8LCDQvdC10LbQtdC70LggJHkgPSB4JC4g0JHQvtC70YzRiNC40YUg0L7RgtC60LvQvtC90LXQvdC40LkgKNC/0L4g0LvQuNC90LjQuCDRgNC10LPRgNC10YHQuNC4INC90LAg0Y3RgtC40YUg0YLQvtGH0LrQsNGFKSDQvdC1INCy0LjQtNC90L4sINGC0L4g0LXRgdGC0Ywg0YHQutCw0LfQsNGC0Ywg0YfRgtC+LdGC0L4g0L4g0LLRi9Cx0YDQvtGB0LDRhSDQvdC10LvRjNC30Y8uCgojIyAxOC4g0JLRi9Cx0YDQvtGB0Ysg0L/QviDQnNCw0YXQsNC70LDQvdC+0LHQuNGB0YMKCmBgYHtyfQpkZi5sb2cubmEuYmVzdC54IDwtIGRmLmxvZy5uYS5iZXN0IHw+IGRwbHlyOjpzZWxlY3QoQlJBSU5fV0UsIFBSRURfSU5EXzIsIFBSRURfSU5EXzMpCgpjb3ZfbWF0cml4IDwtIGNvdihkZi5sb2cubmEuYmVzdC54KQoKbWFoYWxhbm9iaXNfZGlzdGFuY2UgPC0gbWFoYWxhbm9iaXMoZGYubG9nLm5hLmJlc3QueCwgY2VudGVyID0gY29sTWVhbnMoZGYubG9nLm5hLmJlc3QueCksIGNvdiA9IGNvdl9tYXRyaXgpCgpwbG90KG1haGFsYW5vYmlzX2Rpc3RhbmNlLCAKICAgICBtYWluID0gIk1haGFsYW5vYmlzIERpc3RhbmNlIFBsb3QiLAogICAgIHhsYWIgPSAiT2JzZXJ2YXRpb24iLAogICAgIHlsYWIgPSAiTWFoYWxhbm9iaXMgRGlzdGFuY2UiLAogICAgIHR5cGUgPSAiaCIsIAogICAgIGNvbCA9ICJkYXJrYmx1ZSIpIApwbG90KHNvcnQobWFoYWxhbm9iaXNfZGlzdGFuY2UpLCAKICAgICBtYWluID0gIk1haGFsYW5vYmlzIERpc3RhbmNlIFBsb3QiLAogICAgIHhsYWIgPSAiT2JzZXJ2YXRpb24iLAogICAgIHlsYWIgPSAiTWFoYWxhbm9iaXMgRGlzdGFuY2UiLAogICAgIHR5cGUgPSAiaCIsIAogICAgIGNvbCA9ICJkYXJrYmx1ZSIpIApgYGAKCtCf0L4g0JzQsNGF0LDQu9Cw0L3QvtCx0LjRgdGDINCy0LjQtNC10L0g0YHQutCw0YfQvtC6INC00LvRjyDQtNCy0YPRhSDQvdCw0LHQu9GO0LTQtdC90LjQuTogJDEkINC4ICQ0JC4g0K3RgtC+INC00LLQsCDRgdC70L7QvdCwOgoKYGBge3J9CmRmLmxvZy5uYS5iZXN0W2MoMSwgNCksIF0gfD4gcHJpbnRfZGYoKQpgYGAKCiMjIDE5LiDQktGL0LHRgNC+0YHRiyDQv9C+INCa0YPQutGDCgpgYGB7cn0KY29va3NfZGlzdGFuY2UgPC0gY29va3MuZGlzdGFuY2UobW9kZWwuYmVzdCkKCnBsb3QoY29va3NfZGlzdGFuY2UsIAogICAgIGNvbCA9ICJkYXJrYmx1ZSIsCiAgICAgdHlwZSA9ICJoIiwgCiAgICAgbWFpbiA9ICJDb29rJ3MgRGlzdGFuY2UiLAogICAgIHhsYWIgPSAiT2JzZXJ2YXRpb24iLAogICAgIHlsYWIgPSAiQ29vaydzIERpc3RhbmNlIikKcGxvdChzb3J0KGNvb2tzX2Rpc3RhbmNlKSwgCiAgICAgY29sID0gImRhcmtibHVlIiwKICAgICB0eXBlID0gImgiLCAKICAgICBtYWluID0gIkNvb2sncyBEaXN0YW5jZSIsCiAgICAgeGxhYiA9ICJPYnNlcnZhdGlvbiIsCiAgICAgeWxhYiA9ICJDb29rJ3MgRGlzdGFuY2UiKQpgYGAKCtCf0L4g0YHQutCw0YfQutGDINC30L3QsNGH0LXQvdC40Y8g0YDQsNGB0YHRgtC+0Y/QvdC40Y8g0JrRg9C60LAg0LzQvtC20L3QviDQstGL0LTQtdC70LjRgtGMINGC0YDQuCDQvdCw0LHQu9GO0LTQtdC90LjRjzogNiwgMTQg0LggMzEuINCt0YLQviDRgtC1INGB0LDQvNGL0LUg0LvQtdGC0YPRh9C40LUg0LzRi9GI0Lgg0Lgg0LXRidGRINC10YXQuNC00L3QsDoKCmBgYHtyfQpkZi5sb2cubmEuYmVzdFtjKDYsIDE0LCAzMSksIF0gfD4gcHJpbnRfZGYoKQpgYGAKCiMjIDIwLiBTdHVkZW50aXplZCBSZXNpZHVhbHMgdnMgTGV2ZXJhZ2UgUGxvdAoK0J3QsNC60L7QvdC10YYg0L/QvtGB0LzQvtGC0YDQuNC8INC90LAg0YHQvtC+0YLQvdC+0YjQtdC90LjQtSDRgNGL0YfQsNCz0LAg0Lgg0L7RgdGC0LDRgtC60L7QsjoKCmBgYHtyfQpvbHNfcGxvdF9yZXNpZF9sZXYobW9kZWwuYmVzdCkKYGBgCgrQl9C00LXRgdGMINGP0LLQvdGL0YUg0LLRi9Cx0YDQvtGB0L7QsiDQvdC10YI6INC/0L4g0L7RgdGC0LDRgtC60LDQvCwg0LfQsCDQv9GA0LXQtNC10LvQsNC80Lgg0LTQvtCy0LXRgNC40YLQtdC70YzQvdC+0LPQviDQuNC90YLQtdGA0LLQsNC70LAg0L3QtdCx0L7Qu9GM0YjQvtC1INC60L7Qu9C40YfQtdGB0YLQstC+INC90LDQsdC70Y7QtNC10L3QuNC5LCDQv9C+INGA0YvRh9Cw0LPRgyDQtNCy0LAg0LrQsNC90LTQuNC00LDRgtCwINC90LAg0LLRi9Cx0YDQvtGB0YsgLS0tINGB0LvQvtC90YssINCwINC90LDQsdC70Y7QtNC10L3QuNC5LCDQutC+0YLQvtGA0YvQtSDQsdGLINCx0YvQu9C4INC4INC/0L4g0YDRi9GH0LDQs9GDINC4INC/0L4g0L7RgdGC0LDRgtC60LDQvCAi0LLRi9Cx0YDQvtGB0LDQvNC4Iiwg0L3QtdGCLgoKIyMgMjEuINCY0YHQutC70Y7Rh9Cw0LXQvCDQstGL0LHRgNC+0YHRiwoK0J3QsNC50LTQtdC90L3Ri9C1INCy0YvQsdGA0L7RgdGLINC40LzQtdGO0YIg0L7Qv9GA0LXQtNC10LvRkdC90L3Ri9C5INGB0LzRi9GB0LsgLS0tINCx0L7Qu9GM0YjQuNC1INC4INGB0YDQtdC00L3QtSDQsNGC0LDQutGD0LXQvNGL0LUg0YHQu9C+0L3Riywg0LzQsNC70LXQvdGM0LrQuNC1LCDQvdC+INC00L7Qu9Cz0L7QttC40LLRg9GJ0LjQtSDQu9C10YLRg9GH0LjQtSDQvNGL0YjQuCDQuCDRgtCw0LrQsNGPINC20LUg0LXRhdC40LTQvdCwLiDQodC70L7QvdC+0LIg0LjRgdC60LvRjtGH0LDRgtGMINC90LUg0LHRg9C00LXQvCwg0YLQsNC6INC60LDQuiDQvtC90Lgg0L/RgNC+0YHRgtC+INCx0L7Qu9GM0YjQuNC1LCDQv9C+0Y3RgtC+0LzRgyDQnNCw0YXQsNC70LDQvdC+0LHQuNGBINC40YUg0LLRi9C00LXQu9GP0LXRgiwg0L7QtNC90LDQutC+INC+0L3QuCDRhdC+0YDQvtGI0L4g0YHQvtCz0LvQsNGB0YPRjtGC0YHRjyDRgSDQvNC+0LTQtdC70YzRjiAo0LLQuNC00L3QviDQv9C+INC60LDRgNGC0LjQvdC60LUsINC90LDQv9GA0LjQvNC10YApLiDQmNGB0LrQu9GO0YfQuNC8INCy0YvQsdGA0L7RgdGLOgoKYGBge3J9Cm1vZGVsLmJlc3Qub3V0IDwtIGxtKExJRkVTUEFOIH4gQlJBSU5fV0UgKyBQUkVEX0lORF8yICsgUFJFRF9JTkRfMywgZGF0YSA9IGRmLmxvZy5uYS5iZXN0W2MoLTYsIC0xNCwgLTMxKSxdKQptb2RlbC5iZXN0Lm91dC5iZXRhIDwtIGxtLmJldGEobW9kZWwuYmVzdC5vdXQpCnN1bW1hcnkobW9kZWwuYmVzdC5vdXQuYmV0YSkKYGBgCgrQodGD0LTRjyDQv9C+ICRSXjIkINC4INC10LPQviDQuNGB0L/RgNCw0LLQu9C10L3QvdC+0LzRgyDQstCw0YDQuNCw0L3RgtGDLCDQsCDRgtCw0LrQttC1INC/0L4gcC12YWx1ZSDQt9C90LDRh9C40LzQvtGB0YLQuCDRgNC10LPRgNC10YHRgdC40LgsINC/0L7Qu9GD0YfQuNC70Lgg0LHQvtC70LXQtSDRhdC+0YDQvtGI0YPRjiDQvNC+0LTQtdC70YwuCgojIyAyMi4g0J/RgNC10LTRgdC60LDQt9Cw0L3QuNGPCgrQn9GA0LXQtNGB0LrQsNC20LXQvCAqKtCy0YDQtdC80Y8g0LbQuNC30L3QuCoq0L7QsdGL0LrQvdC+0LLQtdC90L3QvtCz0L4g0LXQttCwLiAqKtCS0LXRgSDQtdCz0L4g0LzQvtC30LPQsCoqIC0tLSAkMy4zJCDQsywg0LjQvNC10LXRgiDQt9Cw0YnQuNGC0YMg0L7RgiDRhdC40YnQvdC40LrQvtCyLCDQvdC+INC60YDRg9C/0L3Ri9C1INGF0LjRidC90YvQtSDQv9GC0LjRhtGLINGD0YHQv9C10YjQvdC+INC40YUg0LDRgtCw0LrRg9GO0YIsINC/0L7RjdGC0L7QvNGDICoq0LjQvdC00LXQutGBINGF0LjRidC90LjRh9C10YHRgtCy0LAqKiAtLS0g0L/RgNC40LzQtdGA0L3QviAkMyQuINCSINC90LXQstC+0LvQtSDQtdC20Lgg0LbQuNCy0YPRgiAkNCQtJDYkINC70LXRgi4g0J/Rg9GB0YLRjCDQsdGD0LTQtdGCICQ1JDoKCmBgYHtyfQpkYXRhLmhlZGdlaG9nIDwtIGRhdGEuZnJhbWUoTkFNRSA9ICJFdXJvcGVhbiBoZWRnZWhvZyIsIEJSQUlOX1dFID0gbG9nKDMuMyksIExJRkVTUEFOID0gbG9nKDUpLCBQUkVEX0lORF8yID0gMCwgUFJFRF9JTkRfMyA9IDEpCmRhdGEuaGVkZ2Vob2cgfD4gcHJpbnRfZGYoKQpgYGAKCtCf0YDQtdC00YHQutCw0LbQtdC8INC10LPQviDQv9GA0L7QtNC+0LvQttC40YLQtdC70YzQvdC+0YHRgtGMINC20LjQt9C90Lg6CgpgYGB7cn0KcHJlZC5oZWRnZWhvZy5wcmVkIDwtIHByZWRpY3QobW9kZWwuYmVzdC5vdXQsIG5ld2RhdGEgPSBkYXRhLmhlZGdlaG9nLCBpbnRlcnZhbCA9ICJwcmVkaWN0IikKcHJlZC5oZWRnZWhvZy5jb25mIDwtIHByZWRpY3QobW9kZWwuYmVzdC5vdXQsIG5ld2RhdGEgPSBkYXRhLmhlZGdlaG9nLCBpbnRlcnZhbCA9ICJjb25maWRlbmNlIikKYGBgCgpgYGB7cn0KZXhwKHByZWQuaGVkZ2Vob2cucHJlZFssIDFdKQpgYGAKCtCf0L7Qu9GD0YfQuNC70Lgg0YbQuNGE0YDRiyDQsdC70LjQt9C60LjQtSDQuiDQtNC10LnRgdGC0LLQuNGC0LXQu9GM0L3QvtGB0YLQuC4g0J/QvtGB0LzQvtGC0YDQuNC8INC90LAg0L/RgNC10LTRgdC60LDQt9Cw0YLQtdC70YzQvdGL0Lkg0LjQvdGC0LXRgNCy0LDQuzoKCmBgYHtyfQpwcmludCgi0J3QuNC20L3Rj9GPINCz0YDQsNC90LjRhtCwOiIpCmV4cChwcmVkLmhlZGdlaG9nLnByZWRbLCAyXSkKcHJpbnQoItCS0LXRgNGF0L3Rj9GPINCz0YDQsNC90LjRhtCwOiIpCmV4cChwcmVkLmhlZGdlaG9nLnByZWRbLCAzXSkKYGBgCgrQldC20Lgg0LIg0L3QtdCy0L7Qu9C1INC80L7Qs9GD0YIg0LbQuNGC0Ywg0LTQviAxMCDQu9C10YIuCgrQkCDRgtC10L/QtdGA0Ywg0L3QsCDQtNC+0LLQtdGA0LjRgtC10LvRjNC90YvQuToKCmBgYHtyfQpwcmludCgi0J3QuNC20L3Rj9GPINCz0YDQsNC90LjRhtCwOiIpCmV4cChwcmVkLmhlZGdlaG9nLmNvbmZbLCAyXSkKcHJpbnQoItCS0LXRgNGF0L3Rj9GPINCz0YDQsNC90LjRhtCwOiIpCmV4cChwcmVkLmhlZGdlaG9nLmNvbmZbLCAzXSkKYGBgCg==